@mlightcad/cad-simple-viewer 1.5.9 → 1.5.11

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 (131) hide show
  1. package/dist/cad-simple-viewer.js +12006 -53317
  2. package/dist/cad-simple-viewer.umd.cjs +31 -420
  3. package/dist/mtext-renderer-worker.js +2896 -2767
  4. package/lib/app/AcApDocManager.d.ts +49 -23
  5. package/lib/app/AcApDocManager.d.ts.map +1 -1
  6. package/lib/app/AcApDocManager.js +160 -48
  7. package/lib/app/AcApDocManager.js.map +1 -1
  8. package/lib/app/AcApFontLoader.d.ts +49 -26
  9. package/lib/app/AcApFontLoader.d.ts.map +1 -1
  10. package/lib/app/AcApFontLoader.js +245 -48
  11. package/lib/app/AcApFontLoader.js.map +1 -1
  12. package/lib/app/AcApOpenFileProfiler.d.ts +70 -17
  13. package/lib/app/AcApOpenFileProfiler.d.ts.map +1 -1
  14. package/lib/app/AcApOpenFileProfiler.js +96 -46
  15. package/lib/app/AcApOpenFileProfiler.js.map +1 -1
  16. package/lib/app/AcApOpenFileProgressController.d.ts +22 -5
  17. package/lib/app/AcApOpenFileProgressController.d.ts.map +1 -1
  18. package/lib/app/AcApOpenFileProgressController.js +76 -31
  19. package/lib/app/AcApOpenFileProgressController.js.map +1 -1
  20. package/lib/app/AcApProgress.d.ts +6 -0
  21. package/lib/app/AcApProgress.d.ts.map +1 -1
  22. package/lib/app/AcApProgress.js +11 -0
  23. package/lib/app/AcApProgress.js.map +1 -1
  24. package/lib/app/AcDbOpenDatabaseOptions.d.ts +15 -12
  25. package/lib/app/AcDbOpenDatabaseOptions.d.ts.map +1 -1
  26. package/lib/app/index.d.ts +1 -0
  27. package/lib/app/index.d.ts.map +1 -1
  28. package/lib/app/index.js +1 -0
  29. package/lib/app/index.js.map +1 -1
  30. package/lib/command/convert/AcApConvertToPngCmd.d.ts.map +1 -1
  31. package/lib/command/convert/AcApConvertToPngCmd.js +5 -1
  32. package/lib/command/convert/AcApConvertToPngCmd.js.map +1 -1
  33. package/lib/command/convert/AcApPngConvertor.d.ts +4 -1
  34. package/lib/command/convert/AcApPngConvertor.d.ts.map +1 -1
  35. package/lib/command/convert/AcApPngConvertor.js +131 -78
  36. package/lib/command/convert/AcApPngConvertor.js.map +1 -1
  37. package/lib/editor/input/AcEditor.d.ts +6 -0
  38. package/lib/editor/input/AcEditor.d.ts.map +1 -1
  39. package/lib/editor/input/AcEditor.js +10 -0
  40. package/lib/editor/input/AcEditor.js.map +1 -1
  41. package/lib/editor/input/prompt/AcEdPromptBoxOptions.d.ts +7 -0
  42. package/lib/editor/input/prompt/AcEdPromptBoxOptions.d.ts.map +1 -1
  43. package/lib/editor/input/prompt/AcEdPromptBoxOptions.js +17 -0
  44. package/lib/editor/input/prompt/AcEdPromptBoxOptions.js.map +1 -1
  45. package/lib/editor/input/ui/AcEdInputManager.d.ts +9 -0
  46. package/lib/editor/input/ui/AcEdInputManager.d.ts.map +1 -1
  47. package/lib/editor/input/ui/AcEdInputManager.js +17 -0
  48. package/lib/editor/input/ui/AcEdInputManager.js.map +1 -1
  49. package/lib/editor/view/AcEdSpatialQueryResult.d.ts +10 -0
  50. package/lib/editor/view/AcEdSpatialQueryResult.d.ts.map +1 -1
  51. package/lib/editor/view/AcEdSpatialQueryResult.js +36 -0
  52. package/lib/editor/view/AcEdSpatialQueryResult.js.map +1 -1
  53. package/lib/i18n/cs/command.d.ts +3 -0
  54. package/lib/i18n/cs/command.d.ts.map +1 -1
  55. package/lib/i18n/cs/command.js +3 -0
  56. package/lib/i18n/cs/command.js.map +1 -1
  57. package/lib/i18n/cs/main.d.ts +1 -0
  58. package/lib/i18n/cs/main.d.ts.map +1 -1
  59. package/lib/i18n/cs/main.js +1 -0
  60. package/lib/i18n/cs/main.js.map +1 -1
  61. package/lib/i18n/en/command.d.ts +3 -0
  62. package/lib/i18n/en/command.d.ts.map +1 -1
  63. package/lib/i18n/en/command.js +3 -0
  64. package/lib/i18n/en/command.js.map +1 -1
  65. package/lib/i18n/en/main.d.ts +1 -0
  66. package/lib/i18n/en/main.d.ts.map +1 -1
  67. package/lib/i18n/en/main.js +1 -0
  68. package/lib/i18n/en/main.js.map +1 -1
  69. package/lib/i18n/tr/command.d.ts +3 -0
  70. package/lib/i18n/tr/command.d.ts.map +1 -1
  71. package/lib/i18n/tr/command.js +3 -0
  72. package/lib/i18n/tr/command.js.map +1 -1
  73. package/lib/i18n/tr/main.d.ts +1 -0
  74. package/lib/i18n/tr/main.d.ts.map +1 -1
  75. package/lib/i18n/tr/main.js +1 -0
  76. package/lib/i18n/tr/main.js.map +1 -1
  77. package/lib/i18n/zh/command.d.ts +3 -0
  78. package/lib/i18n/zh/command.d.ts.map +1 -1
  79. package/lib/i18n/zh/command.js +3 -0
  80. package/lib/i18n/zh/command.js.map +1 -1
  81. package/lib/i18n/zh/main.d.ts +1 -0
  82. package/lib/i18n/zh/main.d.ts.map +1 -1
  83. package/lib/i18n/zh/main.js +1 -0
  84. package/lib/i18n/zh/main.js.map +1 -1
  85. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.d.ts.map +1 -1
  86. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js +2 -2
  87. package/lib/spatialIndex/AcTrHierarchicalSpatialIndex.js.map +1 -1
  88. package/lib/spatialIndex/AcTrLinearSpatialIndex.d.ts +6 -1
  89. package/lib/spatialIndex/AcTrLinearSpatialIndex.d.ts.map +1 -1
  90. package/lib/spatialIndex/AcTrLinearSpatialIndex.js +68 -11
  91. package/lib/spatialIndex/AcTrLinearSpatialIndex.js.map +1 -1
  92. package/lib/spatialIndex/AcTrRBushSpatialIndex.d.ts.map +1 -1
  93. package/lib/spatialIndex/AcTrRBushSpatialIndex.js +58 -12
  94. package/lib/spatialIndex/AcTrRBushSpatialIndex.js.map +1 -1
  95. package/lib/util/AcApScriptParser.d.ts +13 -0
  96. package/lib/util/AcApScriptParser.d.ts.map +1 -0
  97. package/lib/util/AcApScriptParser.js +55 -0
  98. package/lib/util/AcApScriptParser.js.map +1 -0
  99. package/lib/util/index.d.ts +1 -0
  100. package/lib/util/index.d.ts.map +1 -1
  101. package/lib/util/index.js +1 -0
  102. package/lib/util/index.js.map +1 -1
  103. package/lib/view/AcTrDirectBatch.d.ts +18 -0
  104. package/lib/view/AcTrDirectBatch.d.ts.map +1 -0
  105. package/lib/view/AcTrDirectBatch.js +113 -0
  106. package/lib/view/AcTrDirectBatch.js.map +1 -0
  107. package/lib/view/AcTrLayer.d.ts +7 -1
  108. package/lib/view/AcTrLayer.d.ts.map +1 -1
  109. package/lib/view/AcTrLayer.js +29 -0
  110. package/lib/view/AcTrLayer.js.map +1 -1
  111. package/lib/view/AcTrLayout.d.ts +11 -1
  112. package/lib/view/AcTrLayout.d.ts.map +1 -1
  113. package/lib/view/AcTrLayout.js +43 -0
  114. package/lib/view/AcTrLayout.js.map +1 -1
  115. package/lib/view/AcTrProgressiveOpenFitController.d.ts +10 -6
  116. package/lib/view/AcTrProgressiveOpenFitController.d.ts.map +1 -1
  117. package/lib/view/AcTrProgressiveOpenFitController.js +35 -76
  118. package/lib/view/AcTrProgressiveOpenFitController.js.map +1 -1
  119. package/lib/view/AcTrScene.d.ts +7 -1
  120. package/lib/view/AcTrScene.d.ts.map +1 -1
  121. package/lib/view/AcTrScene.js +18 -0
  122. package/lib/view/AcTrScene.js.map +1 -1
  123. package/lib/view/AcTrView2d.d.ts +98 -12
  124. package/lib/view/AcTrView2d.d.ts.map +1 -1
  125. package/lib/view/AcTrView2d.js +553 -170
  126. package/lib/view/AcTrView2d.js.map +1 -1
  127. package/lib/view/index.d.ts +1 -0
  128. package/lib/view/index.d.ts.map +1 -1
  129. package/lib/view/index.js +1 -0
  130. package/lib/view/index.js.map +1 -1
  131. package/package.json +10 -8
@@ -96,9 +96,9 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
96
96
  }
97
97
  return to.concat(ar || Array.prototype.slice.call(from));
98
98
  };
99
- import { AcDbAttribute, AcDbBlockReference, acdbHostApplicationServices, AcDbMText, AcDbRasterImage, AcDbRay, AcDbSysVarManager, AcDbViewport, AcDbXline, AcGeBox2d, AcGePoint2d, log } from '@mlightcad/data-model';
99
+ import { AcCmUiYieldGate, AcDbAttribute, AcDbBlockReference, acdbHostApplicationServices, AcDbMText, AcDbRasterImage, AcDbRay, AcDbSysVarManager, AcDbViewport, AcDbXline, AcGeBox2d, AcGePoint2d, log } from '@mlightcad/data-model';
100
100
  import { AcDbSystemVariables } from '@mlightcad/data-model';
101
- import { AcTrEntity, AcTrGroup, AcTrRenderer, AcTrViewportView } from '@mlightcad/three-renderer';
101
+ import { AcTrEntity, AcTrGlyphEntity, AcTrGroup, AcTrRenderer, AcTrViewportView } from '@mlightcad/three-renderer';
102
102
  import { AcTrMatrixUtil } from '@mlightcad/three-renderer';
103
103
  import * as THREE from 'three';
104
104
  import Stats from 'three/examples/jsm/libs/stats.module';
@@ -110,6 +110,7 @@ import { isEffectiveSpatialQueryHit } from '../editor/view/AcEdSpatialQueryResul
110
110
  import { AcTrGeometryUtil } from '../util';
111
111
  import { acapRunDatabaseEdit } from '../util/AcApDatabaseEdit';
112
112
  import { AcEdViewKeyHandler } from './AcEdViewKeyHandler';
113
+ import { shouldExtendBboxForDirectEntity, tryBuildDirectEntityMeta } from './AcTrDirectBatch';
113
114
  import { AcTrEntityDisplayController } from './AcTrEntityDisplayController';
114
115
  import { assertAcTrGroupWcsBboxesConsistent, unionGroupWcsChildBoxes } from './AcTrGroupWcsBboxAssert';
115
116
  import { AcTrInheritedLayerMaterialMapper } from './AcTrInheritedLayerMaterialMapper';
@@ -190,7 +191,7 @@ var AcTrView2d = /** @class */ (function (_super) {
190
191
  * Block table record ids of layouts whose entities are currently being
191
192
  * batch-converted into the scene. Used by
192
193
  * {@link AcTrView2d.loadLayoutEntitiesIfNeeded} to guard against
193
- * re-entrant calls before the `setTimeout` callback flips
194
+ * re-entrant calls before the convert drain flips
194
195
  * `AcTrLayout.isLoaded` to `true`, which would otherwise duplicate
195
196
  * entities when the same layout tab is clicked twice in quick succession.
196
197
  */
@@ -236,10 +237,36 @@ var AcTrView2d = /** @class */ (function (_super) {
236
237
  */
237
238
  _this._convertingLayers = new Set();
238
239
  /**
239
- * When true, entity conversion during document open is deferred across
240
- * event-loop turns so geometry appears incrementally.
240
+ * When true, entity conversion during document open yields cooperatively so
241
+ * geometry paints incrementally while the open overlay is still visible.
241
242
  */
242
243
  _this._progressiveRendering = false;
244
+ /**
245
+ * Serial convert queue for progressive opens. Chunks from `entityAppended`
246
+ * enqueue here and a single drain loop runs {@link batchConvert}, so scene
247
+ * convert overlaps ENTITY flush instead of waiting for a post-open
248
+ * `setTimeout` storm after the loading overlay hides.
249
+ */
250
+ _this._convertQueue = [];
251
+ /** In-flight progressive convert drain; shared so awaiters wait for the queue. */
252
+ _this._convertDrainPromise = null;
253
+ /**
254
+ * Bumped by {@link clear} so an in-flight {@link batchConvert} abandons
255
+ * scene writes and counter updates after the view has been reset for a new
256
+ * document open.
257
+ */
258
+ _this._convertEpoch = 0;
259
+ /** Last time progressive open marked the canvas dirty for paint. */
260
+ _this._lastProgressivePaintAt = 0;
261
+ /** Mid-open WebGL paints while progressive convert was still running. */
262
+ _this._progressivePaintCount = 0;
263
+ /** Cooperative yields taken inside progressive {@link batchConvert}. */
264
+ _this._progressiveYieldCount = 0;
265
+ /**
266
+ * In-flight glyph/group geometry jobs that await fonts via asyncDraw.
267
+ * Counted separately so linework convert can continue while text waits.
268
+ */
269
+ _this._pendingGeometryJobs = 0;
243
270
  _this.animate = function () {
244
271
  var _a;
245
272
  _this._rafId = requestAnimationFrame(_this.animate);
@@ -253,12 +280,20 @@ var AcTrView2d = /** @class */ (function (_super) {
253
280
  return;
254
281
  if (deferRenderWhileLoading)
255
282
  return;
283
+ if (!_this._isDirty)
284
+ return;
285
+ if (_this._progressiveRendering && stillLoading) {
286
+ _this._progressivePaintCount++;
287
+ }
256
288
  var needsRedraw = _this._layoutViewManager.render(_this._scene);
257
289
  if (_this.internalCamera) {
258
290
  _this._css2dRenderer.render(_this._scene.internalScene, _this.internalCamera);
259
291
  }
260
292
  (_a = _this._stats) === null || _a === void 0 ? void 0 : _a.update();
261
- _this._isDirty = (_this._progressiveRendering && stillLoading) || needsRedraw;
293
+ // Do not re-dirty every frame during progressive open — paint is throttled
294
+ // from geometry batches to keep total open time down. Counter hitting 0
295
+ // still forces a final dirty in decreaseNumOfEntitiesToProcess().
296
+ _this._isDirty = needsRedraw;
262
297
  };
263
298
  _this._gripManager = new AcEdGripManager(_this);
264
299
  _this._keyHandler = new AcEdViewKeyHandler(_this);
@@ -482,6 +517,7 @@ var AcTrView2d = /** @class */ (function (_super) {
482
517
  _this._isDirty = true;
483
518
  _this.startAnimationLoop();
484
519
  _this._numOfEntitiesToProcess = 0;
520
+ _this._pendingGeometryJobs = 0;
485
521
  return _this;
486
522
  }
487
523
  AcTrView2d.prototype.getPointerSelectionAction = function (e) {
@@ -564,18 +600,62 @@ var AcTrView2d = /** @class */ (function (_super) {
564
600
  });
565
601
  Object.defineProperty(AcTrView2d.prototype, "isProcessingEntities", {
566
602
  /**
567
- * True while {@link addEntity} batch-conversion callbacks are still running.
603
+ * True while batch conversion or deferred glyph/group geometry is still
604
+ * running.
568
605
  *
569
606
  * Parsing can report 100% before this reaches zero; callers opening files
570
607
  * should wait on this (as {@link zoomToFitDrawing} does) before hiding
571
608
  * progress UI or assuming the canvas is ready.
572
609
  */
573
610
  get: function () {
574
- return this._numOfEntitiesToProcess > 0;
611
+ return this._numOfEntitiesToProcess > 0 || this._pendingGeometryJobs > 0;
575
612
  },
576
613
  enumerable: false,
577
614
  configurable: true
578
615
  });
616
+ /**
617
+ * Waits until batch conversion and deferred glyph/font geometry finish.
618
+ *
619
+ * Document open can resolve before text is drawable (`FontManager.lazyFontLoading`
620
+ * + deferred geometry jobs). Call this before raster/HTML export or scripted
621
+ * commands that assume a complete scene.
622
+ *
623
+ * @param timeoutMs - Maximum wait; returns `false` on timeout (default 60s).
624
+ * @returns `true` when idle, `false` if still busy after the timeout.
625
+ */
626
+ AcTrView2d.prototype.waitUntilIdle = function () {
627
+ return __awaiter(this, arguments, void 0, function (timeoutMs) {
628
+ var deadline, idleStreak;
629
+ if (timeoutMs === void 0) { timeoutMs = 60000; }
630
+ return __generator(this, function (_a) {
631
+ switch (_a.label) {
632
+ case 0:
633
+ deadline = Date.now() + Math.max(0, timeoutMs);
634
+ idleStreak = 0;
635
+ _a.label = 1;
636
+ case 1:
637
+ if (!this.isProcessingEntities) {
638
+ idleStreak++;
639
+ if (idleStreak >= 2) {
640
+ return [2 /*return*/, true];
641
+ }
642
+ }
643
+ else {
644
+ idleStreak = 0;
645
+ }
646
+ if (Date.now() > deadline) {
647
+ return [2 /*return*/, !this.isProcessingEntities];
648
+ }
649
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 16); })];
650
+ case 2:
651
+ _a.sent();
652
+ _a.label = 3;
653
+ case 3: return [3 /*break*/, 1];
654
+ case 4: return [2 /*return*/];
655
+ }
656
+ });
657
+ });
658
+ };
579
659
  Object.defineProperty(AcTrView2d.prototype, "progressiveRendering", {
580
660
  /**
581
661
  * Whether entity conversion during document open is deferred for progressive display.
@@ -585,10 +665,30 @@ var AcTrView2d = /** @class */ (function (_super) {
585
665
  },
586
666
  set: function (value) {
587
667
  this._progressiveRendering = value;
668
+ this.resetProgressiveOpenStats();
588
669
  },
589
670
  enumerable: false,
590
671
  configurable: true
591
672
  });
673
+ Object.defineProperty(AcTrView2d.prototype, "progressiveOpenStats", {
674
+ /**
675
+ * Progressive-open counters for OPENPROF / palette (paints while converting,
676
+ * cooperative yields). Reset when progressive mode is enabled for an open.
677
+ */
678
+ get: function () {
679
+ return {
680
+ paintCount: this._progressivePaintCount,
681
+ yieldCount: this._progressiveYieldCount
682
+ };
683
+ },
684
+ enumerable: false,
685
+ configurable: true
686
+ });
687
+ AcTrView2d.prototype.resetProgressiveOpenStats = function () {
688
+ this._progressivePaintCount = 0;
689
+ this._progressiveYieldCount = 0;
690
+ this._lastProgressivePaintAt = 0;
691
+ };
592
692
  /**
593
693
  * Enables progressive camera framing while entities are batch-converted at
594
694
  * document open. Pair with {@link zoomToFitDrawing} for the final accurate fit.
@@ -946,7 +1046,7 @@ var AcTrView2d = /** @class */ (function (_super) {
946
1046
  */
947
1047
  AcTrView2d.prototype.ensureEntitiesConvertedForExport = function (options) {
948
1048
  return __awaiter(this, void 0, void 0, function () {
949
- var includeInvisibleLayers, db, pending, _a, _b, _c, layoutBtrId, blockTableRecord;
1049
+ var includeInvisibleLayers, db, pending, _a, _b, _c, layoutBtrId, blockTableRecord, idle;
950
1050
  var e_4, _d;
951
1051
  var _this = this;
952
1052
  return __generator(this, function (_e) {
@@ -972,13 +1072,18 @@ var AcTrView2d = /** @class */ (function (_super) {
972
1072
  }
973
1073
  finally { if (e_4) throw e_4.error; }
974
1074
  }
975
- if (pending.length === 0) {
976
- return [2 /*return*/];
977
- }
1075
+ if (!(pending.length > 0)) return [3 /*break*/, 2];
978
1076
  this._numOfEntitiesToProcess += pending.length;
979
1077
  return [4 /*yield*/, this.batchConvert(pending, { forExport: true })];
980
1078
  case 1:
981
1079
  _e.sent();
1080
+ _e.label = 2;
1081
+ case 2: return [4 /*yield*/, this.waitUntilIdle()];
1082
+ case 3:
1083
+ idle = _e.sent();
1084
+ if (!idle) {
1085
+ log.warn('[AcTrView2d] Timed out waiting for deferred geometry before export');
1086
+ }
982
1087
  return [2 /*return*/];
983
1088
  }
984
1089
  });
@@ -1078,7 +1183,9 @@ var AcTrView2d = /** @class */ (function (_super) {
1078
1183
  AcTrView2d.prototype.zoomToFitDrawing = function (timeout, layoutBtrId) {
1079
1184
  var _this = this;
1080
1185
  if (timeout === void 0) { timeout = 0; }
1081
- var waiter = new AcEdConditionWaiter(function () { return _this._numOfEntitiesToProcess <= 0; }, function () {
1186
+ var waiter = new AcEdConditionWaiter(
1187
+ // Include deferred glyph/group jobs so text extents land before final fit.
1188
+ function () { return !_this.isProcessingEntities; }, function () {
1082
1189
  if (layoutBtrId && _this._externallyFramedLayouts.delete(layoutBtrId)) {
1083
1190
  _this.endProgressiveOpenFit();
1084
1191
  return;
@@ -1466,16 +1573,34 @@ var AcTrView2d = /** @class */ (function (_super) {
1466
1573
  * @param entity Input one or multiple transient entities
1467
1574
  */
1468
1575
  AcTrView2d.prototype.addTransientEntity = function (entity) {
1576
+ var _this = this;
1469
1577
  var entities = Array.isArray(entity) ? entity : [entity];
1470
- for (var i = 0; i < entities.length; ++i) {
1578
+ var epoch = this._convertEpoch;
1579
+ var _loop_2 = function (i) {
1471
1580
  var entity_1 = entities[i];
1472
- var threeEntity = this.drawEntity(entity_1, true);
1581
+ var threeEntity = this_1.drawEntity(entity_1, true);
1473
1582
  if (threeEntity) {
1474
1583
  threeEntity.objectId = entity_1.objectId;
1475
- threeEntity.syncDraw();
1476
- this._scene.addTransientEntity(threeEntity);
1477
- this._isDirty = true;
1584
+ void threeEntity
1585
+ .asyncDraw()
1586
+ .then(function () {
1587
+ // Drop stale transients started before clear()/regen invalidated the view.
1588
+ if (epoch !== _this._convertEpoch) {
1589
+ threeEntity.dispose();
1590
+ return;
1591
+ }
1592
+ _this._scene.addTransientEntity(threeEntity);
1593
+ _this._isDirty = true;
1594
+ })
1595
+ .catch(function (error) {
1596
+ log.error('[AcTrView2d] Transient entity geometry failed:', error);
1597
+ threeEntity.dispose();
1598
+ });
1478
1599
  }
1600
+ };
1601
+ var this_1 = this;
1602
+ for (var i = 0; i < entities.length; ++i) {
1603
+ _loop_2(i);
1479
1604
  }
1480
1605
  };
1481
1606
  /**
@@ -1533,25 +1658,15 @@ var AcTrView2d = /** @class */ (function (_super) {
1533
1658
  * @inheritdoc
1534
1659
  */
1535
1660
  AcTrView2d.prototype.addEntity = function (entity) {
1536
- var _this = this;
1661
+ var _a;
1537
1662
  var entities = Array.isArray(entity) ? entity : [entity];
1538
1663
  this._numOfEntitiesToProcess += entities.length;
1539
- var convert = function () { return __awaiter(_this, void 0, void 0, function () {
1540
- return __generator(this, function (_a) {
1541
- switch (_a.label) {
1542
- case 0: return [4 /*yield*/, this.batchConvert(entities)];
1543
- case 1:
1544
- _a.sent();
1545
- return [2 /*return*/];
1546
- }
1547
- });
1548
- }); };
1549
1664
  if (this._progressiveRendering) {
1550
- setTimeout(convert);
1551
- this._isDirty = true;
1665
+ (_a = this._convertQueue).push.apply(_a, __spreadArray([], __read(entities), false));
1666
+ void this.drainConvertQueue();
1552
1667
  }
1553
1668
  else {
1554
- void convert();
1669
+ void this.batchConvert(entities);
1555
1670
  }
1556
1671
  };
1557
1672
  /**
@@ -1629,12 +1744,23 @@ var AcTrView2d = /** @class */ (function (_super) {
1629
1744
  }
1630
1745
  // Reconvert through the same path as initial load so block references are
1631
1746
  // split by layer correctly and deferred MTEXT/SHAPE geometry is drawn.
1632
- void this.batchConvert(entities).then(function () {
1633
- if (selectedIds.length > 0) {
1634
- _this.highlight(selectedIds);
1635
- }
1636
- _this._gripManager.refresh();
1637
- });
1747
+ void (function () { return __awaiter(_this, void 0, void 0, function () {
1748
+ return __generator(this, function (_a) {
1749
+ switch (_a.label) {
1750
+ case 0: return [4 /*yield*/, this.batchConvert(entities)];
1751
+ case 1:
1752
+ _a.sent();
1753
+ return [4 /*yield*/, this.waitUntilDeferredGeometryIdle()];
1754
+ case 2:
1755
+ _a.sent();
1756
+ if (selectedIds.length > 0) {
1757
+ this.highlight(selectedIds);
1758
+ }
1759
+ this._gripManager.refresh();
1760
+ return [2 /*return*/];
1761
+ }
1762
+ });
1763
+ }); })();
1638
1764
  this._isDirty = true;
1639
1765
  // Not sure why texture for image entity isn't updated even if 'isDirty' flag is already set to true.
1640
1766
  // So add one timeout event to set 'isDirty' flag to true again to make it work
@@ -1743,16 +1869,17 @@ var AcTrView2d = /** @class */ (function (_super) {
1743
1869
  * **Critically, this runs through `AcEdConditionWaiter`**: at the
1744
1870
  * moment `layoutSwitched` fires, the layout's entities (including its
1745
1871
  * `AcDbViewport`s) have not yet been batch-converted into the scene
1746
- * — `loadLayoutEntitiesIfNeeded` chunked-converts via `setTimeout`.
1872
+ * — `loadLayoutEntitiesIfNeeded` chunked-converts via the progressive
1873
+ * convert queue (or direct `batchConvert`).
1747
1874
  * Without the waiter, `viewportsBoundingBox` returns undefined and
1748
1875
  * the strategy degrades into (1) zooming to garbage `limits`, or
1749
1876
  * (4) zooming to an empty scene box. The waiter polls
1750
- * `_numOfEntitiesToProcess` and only fires the heuristic once the
1751
- * conversion is done.
1877
+ * {@link isProcessingEntities} (convert queue + deferred glyph jobs)
1878
+ * and only fires the heuristic once conversion and text geometry finish.
1752
1879
  */
1753
1880
  AcTrView2d.prototype.applyInitialZoom = function (btrId, layout) {
1754
1881
  var _this = this;
1755
- var waiter = new AcEdConditionWaiter(function () { return _this._numOfEntitiesToProcess <= 0; }, function () {
1882
+ var waiter = new AcEdConditionWaiter(function () { return !_this.isProcessingEntities; }, function () {
1756
1883
  if (_this._externallyFramedLayouts.delete(btrId)) {
1757
1884
  return;
1758
1885
  }
@@ -1793,11 +1920,80 @@ var AcTrView2d = /** @class */ (function (_super) {
1793
1920
  * @inheritdoc
1794
1921
  */
1795
1922
  AcTrView2d.prototype.clear = function () {
1923
+ // Invalidate any in-flight progressive convert so it neither paints into
1924
+ // the cleared scene nor double-decrements the processing counter.
1925
+ this._convertEpoch++;
1926
+ this._convertQueue.length = 0;
1927
+ this._numOfEntitiesToProcess = 0;
1928
+ this._pendingGeometryJobs = 0;
1796
1929
  this._scene.clear();
1797
1930
  this._isDirty = true;
1798
1931
  this._missedImages.clear();
1799
1932
  this._renderer.dispose();
1800
1933
  };
1934
+ /**
1935
+ * Drains the progressive convert queue on a single serial worker so ENTITY
1936
+ * flush chunks can enqueue while conversion overlaps the loading overlay.
1937
+ *
1938
+ * Concurrent callers share the same promise; if more entities are queued
1939
+ * after a drain finishes, a follow-up drain is started.
1940
+ */
1941
+ AcTrView2d.prototype.drainConvertQueue = function () {
1942
+ return __awaiter(this, void 0, void 0, function () {
1943
+ var _this = this;
1944
+ return __generator(this, function (_a) {
1945
+ switch (_a.label) {
1946
+ case 0:
1947
+ if (!this._convertDrainPromise) return [3 /*break*/, 4];
1948
+ return [4 /*yield*/, this._convertDrainPromise];
1949
+ case 1:
1950
+ _a.sent();
1951
+ if (!(this._convertQueue.length > 0)) return [3 /*break*/, 3];
1952
+ return [4 /*yield*/, this.drainConvertQueue()];
1953
+ case 2:
1954
+ _a.sent();
1955
+ _a.label = 3;
1956
+ case 3: return [2 /*return*/];
1957
+ case 4:
1958
+ this._convertDrainPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
1959
+ var batch;
1960
+ return __generator(this, function (_a) {
1961
+ switch (_a.label) {
1962
+ case 0:
1963
+ if (!(this._convertQueue.length > 0)) return [3 /*break*/, 2];
1964
+ batch = this._convertQueue.splice(0, this._convertQueue.length);
1965
+ return [4 /*yield*/, this.batchConvert(batch)];
1966
+ case 1:
1967
+ _a.sent();
1968
+ return [3 /*break*/, 0];
1969
+ case 2: return [2 /*return*/];
1970
+ }
1971
+ });
1972
+ }); })().finally(function () {
1973
+ _this._convertDrainPromise = null;
1974
+ });
1975
+ return [4 /*yield*/, this._convertDrainPromise];
1976
+ case 5:
1977
+ _a.sent();
1978
+ return [2 /*return*/];
1979
+ }
1980
+ });
1981
+ });
1982
+ };
1983
+ /**
1984
+ * Marks the canvas dirty for progressive open, throttled to avoid painting
1985
+ * on every entity (which dominated total open time).
1986
+ */
1987
+ AcTrView2d.prototype.markProgressiveDirty = function (force) {
1988
+ if (force === void 0) { force = false; }
1989
+ var now = performance.now();
1990
+ if (force ||
1991
+ now - this._lastProgressivePaintAt >=
1992
+ AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS) {
1993
+ this._isDirty = true;
1994
+ this._lastProgressivePaintAt = now;
1995
+ }
1996
+ };
1801
1997
  /**
1802
1998
  * @inheritdoc
1803
1999
  */
@@ -1886,7 +2082,7 @@ var AcTrView2d = /** @class */ (function (_super) {
1886
2082
  * on it would silently miss layouts that are pre-loaded ahead of
1887
2083
  * becoming active (e.g. background prefetch).
1888
2084
  * 2. The `_loadingLayouts` guard prevents re-entrance while the
1889
- * `setTimeout` chunked-convert callback is still in flight. Without it,
2085
+ * convert drain is still in flight. Without it,
1890
2086
  * clicking the same layout tab twice in quick succession (or
1891
2087
  * `layoutSwitched` firing twice during the async window) would iterate
1892
2088
  * the block table record again and duplicate every entity in the
@@ -1988,31 +2184,35 @@ var AcTrView2d = /** @class */ (function (_super) {
1988
2184
  this._numOfEntitiesToProcess += entities_2.length;
1989
2185
  var convert = function () { return __awaiter(_this, void 0, void 0, function () {
1990
2186
  var layout;
1991
- return __generator(this, function (_a) {
1992
- switch (_a.label) {
2187
+ var _a;
2188
+ return __generator(this, function (_b) {
2189
+ switch (_b.label) {
1993
2190
  case 0:
1994
- _a.trys.push([0, , 2, 3]);
1995
- return [4 /*yield*/, this.batchConvert(entities_2)];
2191
+ _b.trys.push([0, , 5, 6]);
2192
+ if (!this._progressiveRendering) return [3 /*break*/, 2];
2193
+ (_a = this._convertQueue).push.apply(_a, __spreadArray([], __read(entities_2), false));
2194
+ return [4 /*yield*/, this.drainConvertQueue()];
1996
2195
  case 1:
1997
- _a.sent();
2196
+ _b.sent();
2197
+ return [3 /*break*/, 4];
2198
+ case 2: return [4 /*yield*/, this.batchConvert(entities_2)];
2199
+ case 3:
2200
+ _b.sent();
2201
+ _b.label = 4;
2202
+ case 4:
1998
2203
  layout = this._scene.layouts.get(layoutBtrId);
1999
2204
  if (layout) {
2000
2205
  layout.isLoaded = true;
2001
2206
  }
2002
- return [3 /*break*/, 3];
2003
- case 2:
2207
+ return [3 /*break*/, 6];
2208
+ case 5:
2004
2209
  this._loadingLayouts.delete(layoutBtrId);
2005
2210
  return [7 /*endfinally*/];
2006
- case 3: return [2 /*return*/];
2211
+ case 6: return [2 /*return*/];
2007
2212
  }
2008
2213
  });
2009
2214
  }); };
2010
- if (this._progressiveRendering) {
2011
- setTimeout(convert);
2012
- }
2013
- else {
2014
- void convert();
2015
- }
2215
+ void convert();
2016
2216
  }
2017
2217
  catch (error) {
2018
2218
  log.error('[AcTrView2d] Error loading layout entities:', error);
@@ -2087,21 +2287,25 @@ var AcTrView2d = /** @class */ (function (_super) {
2087
2287
  return entity.worldDraw(this._renderer, delay);
2088
2288
  };
2089
2289
  /**
2090
- * Finishes geometry for a converted entity. Block groups always use
2091
- * {@link AcTrGroup.syncDraw} to finalize deferred children. Progressive mode
2092
- * defers MTEXT/SHAPE to async workers; non-progressive mode uses
2093
- * {@link AcTrEntity.syncDraw}.
2290
+ * Finishes geometry for a converted entity.
2291
+ *
2292
+ * Glyph entities and block groups use {@link AcTrEntity.asyncDraw} so
2293
+ * {@link FontManager.awaitFontsBeforeDraw} can wait for fonts without
2294
+ * relying on a full-scene regen. Other entities keep the sync finalize path.
2094
2295
  */
2095
- AcTrView2d.prototype.finishEntityGeometry = function (threeEntity, progressive) {
2296
+ AcTrView2d.prototype.finishEntityGeometry = function (threeEntity, _progressive) {
2096
2297
  return __awaiter(this, void 0, void 0, function () {
2097
2298
  return __generator(this, function (_a) {
2098
2299
  switch (_a.label) {
2099
2300
  case 0:
2100
- if (threeEntity instanceof AcTrGroup) {
2101
- threeEntity.syncDraw();
2301
+ if (!(threeEntity instanceof AcTrGroup)) return [3 /*break*/, 2];
2302
+ // Compacted INSERT templates may skip syncDraw when fonts are awaited
2303
+ // later; still walk for empty glyph shells. Skip only when there is
2304
+ // nothing left to finalize (incl. post-cache ATTRIBs).
2305
+ if (threeEntity.getSourceEntities().length === 0 &&
2306
+ !this.groupHasPendingGlyphGeometry(threeEntity)) {
2102
2307
  return [2 /*return*/];
2103
2308
  }
2104
- if (!progressive) return [3 /*break*/, 2];
2105
2309
  return [4 /*yield*/, threeEntity.asyncDraw()];
2106
2310
  case 1:
2107
2311
  _a.sent();
@@ -2110,12 +2314,82 @@ var AcTrView2d = /** @class */ (function (_super) {
2110
2314
  if (threeEntity.hasDrawableGeometry()) {
2111
2315
  return [2 /*return*/];
2112
2316
  }
2113
- threeEntity.syncDraw();
2317
+ return [4 /*yield*/, threeEntity.asyncDraw()];
2318
+ case 3:
2319
+ _a.sent();
2114
2320
  return [2 /*return*/];
2115
2321
  }
2116
2322
  });
2117
2323
  });
2118
2324
  };
2325
+ AcTrView2d.prototype.needsDeferredFontGeometry = function (threeEntity) {
2326
+ return (threeEntity instanceof AcTrGlyphEntity ||
2327
+ threeEntity instanceof AcTrGroup);
2328
+ };
2329
+ AcTrView2d.prototype.groupHasPendingGlyphGeometry = function (group) {
2330
+ var pending = false;
2331
+ group.traverse(function (child) {
2332
+ if (child instanceof AcTrGlyphEntity &&
2333
+ !child.hasDrawableGeometry()) {
2334
+ pending = true;
2335
+ }
2336
+ });
2337
+ return pending;
2338
+ };
2339
+ /**
2340
+ * Runs glyph/group geometry finalize off the main convert loop so other
2341
+ * entities keep converting while fonts download.
2342
+ */
2343
+ AcTrView2d.prototype.enqueueDeferredGeometry = function (run, epoch) {
2344
+ var _this = this;
2345
+ if (epoch !== this._convertEpoch) {
2346
+ return;
2347
+ }
2348
+ this._pendingGeometryJobs++;
2349
+ void run()
2350
+ .then(function () {
2351
+ // Convert counter often hits 0 before fonts finish; without this,
2352
+ // text added later never paints until the user pans/zooms.
2353
+ if (epoch === _this._convertEpoch) {
2354
+ _this._isDirty = true;
2355
+ }
2356
+ })
2357
+ .catch(function (error) {
2358
+ log.error('[AcTrView2d] Deferred entity geometry failed:', error);
2359
+ })
2360
+ .finally(function () {
2361
+ if (epoch === _this._convertEpoch) {
2362
+ _this._pendingGeometryJobs = Math.max(0, _this._pendingGeometryJobs - 1);
2363
+ if (_this._pendingGeometryJobs === 0) {
2364
+ _this._isDirty = true;
2365
+ }
2366
+ }
2367
+ });
2368
+ };
2369
+ /**
2370
+ * Waits until side-pool glyph/group jobs for the current convert epoch finish.
2371
+ * Used by entity updates that must highlight after text is in the scene.
2372
+ */
2373
+ AcTrView2d.prototype.waitUntilDeferredGeometryIdle = function () {
2374
+ return __awaiter(this, void 0, void 0, function () {
2375
+ var epoch;
2376
+ return __generator(this, function (_a) {
2377
+ switch (_a.label) {
2378
+ case 0:
2379
+ epoch = this._convertEpoch;
2380
+ _a.label = 1;
2381
+ case 1:
2382
+ if (!(epoch === this._convertEpoch &&
2383
+ this._pendingGeometryJobs > 0)) return [3 /*break*/, 3];
2384
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
2385
+ case 2:
2386
+ _a.sent();
2387
+ return [3 /*break*/, 1];
2388
+ case 3: return [2 /*return*/];
2389
+ }
2390
+ });
2391
+ });
2392
+ };
2119
2393
  /**
2120
2394
  * Walks the given block table record once and creates one
2121
2395
  * `AcTrViewportView` for every real `AcDbViewport` entity it finds
@@ -2163,116 +2437,206 @@ var AcTrView2d = /** @class */ (function (_super) {
2163
2437
  */
2164
2438
  AcTrView2d.prototype.batchConvert = function (entities_3) {
2165
2439
  return __awaiter(this, arguments, void 0, function (entities, options) {
2166
- var progressive, i, entity, shouldConvert, threeEntity, isExtendBbox, layoutView, viewportView, fileName, error_2;
2440
+ var epoch, progressive, yieldGate, yieldToEventLoop, _loop_3, this_2, i;
2167
2441
  var _this = this;
2168
2442
  if (options === void 0) { options = {}; }
2169
2443
  return __generator(this, function (_a) {
2170
2444
  switch (_a.label) {
2171
2445
  case 0:
2446
+ epoch = this._convertEpoch;
2172
2447
  progressive = this._progressiveRendering && !options.forExport;
2448
+ yieldGate = progressive
2449
+ ? new AcCmUiYieldGate(AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS)
2450
+ : undefined;
2451
+ yieldToEventLoop = function () {
2452
+ return new Promise(function (resolve) { return setTimeout(resolve, 0); });
2453
+ };
2454
+ _loop_3 = function (i) {
2455
+ var entity, shouldConvert, directMeta, added, threeEntity_1, isMultiLayerGroup, deferGeometry, isExtendBbox_1, commitEntity, layoutView, viewportView, fileName, error_2, didYield;
2456
+ return __generator(this, function (_b) {
2457
+ switch (_b.label) {
2458
+ case 0:
2459
+ entity = entities[i];
2460
+ _b.label = 1;
2461
+ case 1:
2462
+ _b.trys.push([1, 9, 10, 11]);
2463
+ // Document was cleared / replaced while this batch was draining.
2464
+ if (epoch !== this_2._convertEpoch) {
2465
+ return [2 /*return*/, "continue"];
2466
+ }
2467
+ // Skip the default paper-space viewport (`*Paper_Space`) entirely:
2468
+ // it is an AutoCAD-internal viewport that exists in every paper
2469
+ // layout and must not be drawn (would render a giant rectangle in
2470
+ // the paper coordinate system), nor added to the spatial index
2471
+ // (would stretch the layout's bounding box and break
2472
+ // zoomToFitDrawing), nor turned into an AcTrViewportView (would
2473
+ // setScissor over most of the canvas and squeeze the real user
2474
+ // viewports into a corner). See
2475
+ // `AcTrViewportView.isDefaultPaperSpaceViewport` for the criterion
2476
+ // and the rationale (legacy `number === 1` is unreliable across
2477
+ // parsers).
2478
+ if (entity instanceof AcDbViewport &&
2479
+ AcTrViewportView.isDefaultPaperSpaceViewport(entity)) {
2480
+ return [2 /*return*/, "continue"];
2481
+ }
2482
+ shouldConvert = options.forExport
2483
+ ? this_2._entityDisplay.shouldConvertForExport(entity)
2484
+ : this_2._entityDisplay.shouldConvert(entity);
2485
+ if (!shouldConvert) {
2486
+ return [2 /*return*/, "continue"];
2487
+ }
2488
+ directMeta = tryBuildDirectEntityMeta(entity, this_2._renderer);
2489
+ if (directMeta) {
2490
+ added = false;
2491
+ try {
2492
+ added = this_2._scene.addDirectEntity(directMeta, shouldExtendBboxForDirectEntity(entity));
2493
+ if (added) {
2494
+ this_2.applySessionHiddenObjectState(entity.objectId);
2495
+ if (progressive) {
2496
+ this_2.markProgressiveDirty();
2497
+ this_2._progressiveOpenFit.afterGeometryBatch(function () { return _this.resolveLayoutFitBox(); }, i);
2498
+ }
2499
+ }
2500
+ }
2501
+ finally {
2502
+ directMeta.geometry.dispose();
2503
+ }
2504
+ if (added) {
2505
+ return [2 /*return*/, "continue"];
2506
+ }
2507
+ // Append refused (e.g. invisible) — fall through to the legacy path.
2508
+ }
2509
+ threeEntity_1 = this_2.drawEntity(entity, false);
2510
+ // Viewports may produce no border geometry (e.g. on a no-plot layer) while
2511
+ // still needing an AcTrViewportView for model content below.
2512
+ if (!threeEntity_1 && !(entity instanceof AcDbViewport))
2513
+ return [2 /*return*/, "continue"];
2514
+ if (!threeEntity_1) return [3 /*break*/, 8];
2515
+ threeEntity_1.objectId = entity.objectId;
2516
+ threeEntity_1.ownerId = entity.ownerId;
2517
+ threeEntity_1.layerName = entity.layer;
2518
+ threeEntity_1.visible = entity.visibility !== false;
2519
+ if (threeEntity_1 instanceof AcTrGroup &&
2520
+ threeEntity_1.isOnTheSameLayer) {
2521
+ // Even when a block expands to a single layer bucket, children authored on
2522
+ // layer "0" still inherit the INSERT layer for ByLayer traits (color, etc.).
2523
+ this_2._inheritedLayerMaterialMapper.remap(threeEntity_1.children, '0', threeEntity_1.layerName);
2524
+ threeEntity_1.userData.insertLayerName = threeEntity_1.layerName;
2525
+ }
2526
+ isMultiLayerGroup = threeEntity_1 instanceof AcTrGroup &&
2527
+ !threeEntity_1.isOnTheSameLayer;
2528
+ deferGeometry = !options.forExport && this_2.needsDeferredFontGeometry(threeEntity_1);
2529
+ if (!isMultiLayerGroup) return [3 /*break*/, 5];
2530
+ if (!deferGeometry) return [3 /*break*/, 2];
2531
+ this_2.enqueueDeferredGeometry(function () {
2532
+ return _this.handleGroup(threeEntity_1, progressive, epoch);
2533
+ }, epoch);
2534
+ return [3 /*break*/, 4];
2535
+ case 2: return [4 /*yield*/, this_2.handleGroup(threeEntity_1, progressive, epoch)];
2536
+ case 3:
2537
+ _b.sent();
2538
+ _b.label = 4;
2539
+ case 4: return [3 /*break*/, 8];
2540
+ case 5:
2541
+ isExtendBbox_1 = !(entity instanceof AcDbRay || entity instanceof AcDbXline);
2542
+ commitEntity = function () { return __awaiter(_this, void 0, void 0, function () {
2543
+ var _this = this;
2544
+ return __generator(this, function (_a) {
2545
+ switch (_a.label) {
2546
+ case 0: return [4 /*yield*/, this.finishEntityGeometry(threeEntity_1, progressive)];
2547
+ case 1:
2548
+ _a.sent();
2549
+ if (epoch !== this._convertEpoch) {
2550
+ threeEntity_1.dispose();
2551
+ return [2 /*return*/];
2552
+ }
2553
+ if (threeEntity_1 instanceof AcTrGroup) {
2554
+ this.syncGroupSpatialBoundsForIndexing(threeEntity_1);
2555
+ }
2556
+ this._scene.addEntity(threeEntity_1, isExtendBbox_1);
2557
+ this.applySessionHiddenObjectState(entity.objectId);
2558
+ // Release memory occupied by this entity
2559
+ threeEntity_1.dispose();
2560
+ if (progressive) {
2561
+ this.markProgressiveDirty();
2562
+ this._progressiveOpenFit.afterGeometryBatch(function () { return _this.resolveLayoutFitBox(); }, i);
2563
+ }
2564
+ return [2 /*return*/];
2565
+ }
2566
+ });
2567
+ }); };
2568
+ if (!deferGeometry) return [3 /*break*/, 6];
2569
+ this_2.enqueueDeferredGeometry(commitEntity, epoch);
2570
+ return [3 /*break*/, 8];
2571
+ case 6: return [4 /*yield*/, commitEntity()];
2572
+ case 7:
2573
+ _b.sent();
2574
+ _b.label = 8;
2575
+ case 8:
2576
+ if (epoch !== this_2._convertEpoch) {
2577
+ return [2 /*return*/, "continue"];
2578
+ }
2579
+ if (entity instanceof AcDbViewport) {
2580
+ // Default paper-space viewport was already filtered out at the
2581
+ // top of the loop, so anything that reaches here is a real
2582
+ // user-created viewport. The redundant check below is kept as
2583
+ // a defensive guard in case a future refactor reorders the
2584
+ // early-skip — it costs ~nothing and prevents a regression.
2585
+ if (!AcTrViewportView.isDefaultPaperSpaceViewport(entity)) {
2586
+ layoutView = this_2._layoutViewManager.getAt(entity.ownerId);
2587
+ if (layoutView) {
2588
+ viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this_2._renderer);
2589
+ layoutView.addViewport(viewportView);
2590
+ }
2591
+ }
2592
+ }
2593
+ else if (entity instanceof AcDbRasterImage) {
2594
+ fileName = entity.imageFileName;
2595
+ if (fileName && !entity.image) {
2596
+ this_2._missedImages.set(entity.objectId, fileName);
2597
+ }
2598
+ }
2599
+ return [3 /*break*/, 11];
2600
+ case 9:
2601
+ error_2 = _b.sent();
2602
+ log.error("[AcTrView2d] Failed to convert entity ".concat(entity.objectId, " (").concat(entity.type, "):"), error_2);
2603
+ return [3 /*break*/, 11];
2604
+ case 10:
2605
+ // Counter was reset in clear() when the epoch advanced; do not
2606
+ // decrease again or isProcessingEntities can go negative/warn.
2607
+ if (epoch === this_2._convertEpoch) {
2608
+ this_2.decreaseNumOfEntitiesToProcess();
2609
+ }
2610
+ return [7 /*endfinally*/];
2611
+ case 11:
2612
+ if (epoch !== this_2._convertEpoch) {
2613
+ return [2 /*return*/, "continue"];
2614
+ }
2615
+ if (!yieldGate) return [3 /*break*/, 13];
2616
+ return [4 /*yield*/, yieldGate.maybeYield(yieldToEventLoop)];
2617
+ case 12:
2618
+ didYield = _b.sent();
2619
+ if (didYield) {
2620
+ this_2._progressiveYieldCount++;
2621
+ }
2622
+ _b.label = 13;
2623
+ case 13: return [2 /*return*/];
2624
+ }
2625
+ });
2626
+ };
2627
+ this_2 = this;
2173
2628
  i = 0;
2174
2629
  _a.label = 1;
2175
2630
  case 1:
2176
- if (!(i < entities.length)) return [3 /*break*/, 11];
2177
- entity = entities[i];
2178
- _a.label = 2;
2631
+ if (!(i < entities.length)) return [3 /*break*/, 4];
2632
+ return [5 /*yield**/, _loop_3(i)];
2179
2633
  case 2:
2180
- _a.trys.push([2, 8, 9, 10]);
2181
- // Skip the default paper-space viewport (`*Paper_Space`) entirely:
2182
- // it is an AutoCAD-internal viewport that exists in every paper
2183
- // layout and must not be drawn (would render a giant rectangle in
2184
- // the paper coordinate system), nor added to the spatial index
2185
- // (would stretch the layout's bounding box and break
2186
- // zoomToFitDrawing), nor turned into an AcTrViewportView (would
2187
- // setScissor over most of the canvas and squeeze the real user
2188
- // viewports into a corner). See
2189
- // `AcTrViewportView.isDefaultPaperSpaceViewport` for the criterion
2190
- // and the rationale (legacy `number === 1` is unreliable across
2191
- // parsers).
2192
- if (entity instanceof AcDbViewport &&
2193
- AcTrViewportView.isDefaultPaperSpaceViewport(entity)) {
2194
- return [3 /*break*/, 10];
2195
- }
2196
- shouldConvert = options.forExport
2197
- ? this._entityDisplay.shouldConvertForExport(entity)
2198
- : this._entityDisplay.shouldConvert(entity);
2199
- if (!shouldConvert) {
2200
- return [3 /*break*/, 10];
2201
- }
2202
- threeEntity = this.drawEntity(entity, progressive);
2203
- // Viewports may produce no border geometry (e.g. on a no-plot layer) while
2204
- // still needing an AcTrViewportView for model content below.
2205
- if (!threeEntity && !(entity instanceof AcDbViewport))
2206
- return [3 /*break*/, 10];
2207
- if (!threeEntity) return [3 /*break*/, 7];
2208
- threeEntity.objectId = entity.objectId;
2209
- threeEntity.ownerId = entity.ownerId;
2210
- threeEntity.layerName = entity.layer;
2211
- threeEntity.visible = entity.visibility !== false;
2212
- if (threeEntity instanceof AcTrGroup &&
2213
- threeEntity.isOnTheSameLayer) {
2214
- // Even when a block expands to a single layer bucket, children authored on
2215
- // layer "0" still inherit the INSERT layer for ByLayer traits (color, etc.).
2216
- this._inheritedLayerMaterialMapper.remap(threeEntity.children, '0', threeEntity.layerName);
2217
- threeEntity.userData.insertLayerName = threeEntity.layerName;
2218
- }
2219
- if (!(threeEntity instanceof AcTrGroup &&
2220
- !threeEntity.isOnTheSameLayer)) return [3 /*break*/, 4];
2221
- return [4 /*yield*/, this.handleGroup(threeEntity, progressive)];
2222
- case 3:
2223
2634
  _a.sent();
2224
- return [3 /*break*/, 7];
2225
- case 4:
2226
- isExtendBbox = !(entity instanceof AcDbRay || entity instanceof AcDbXline);
2227
- return [4 /*yield*/, this.finishEntityGeometry(threeEntity, progressive)];
2228
- case 5:
2229
- _a.sent();
2230
- if (threeEntity instanceof AcTrGroup) {
2231
- this.syncGroupSpatialBoundsForIndexing(threeEntity);
2232
- }
2233
- this._scene.addEntity(threeEntity, isExtendBbox);
2234
- this.applySessionHiddenObjectState(entity.objectId);
2235
- // Release memory occupied by this entity
2236
- threeEntity.dispose();
2237
- if (!progressive) return [3 /*break*/, 7];
2238
- this._isDirty = true;
2239
- return [4 /*yield*/, this._progressiveOpenFit.afterGeometryBatch(function () { return _this.resolveLayoutFitBox(); }, i)];
2240
- case 6:
2241
- _a.sent();
2242
- _a.label = 7;
2243
- case 7:
2244
- if (entity instanceof AcDbViewport) {
2245
- // Default paper-space viewport was already filtered out at the
2246
- // top of the loop, so anything that reaches here is a real
2247
- // user-created viewport. The redundant check below is kept as
2248
- // a defensive guard in case a future refactor reorders the
2249
- // early-skip — it costs ~nothing and prevents a regression.
2250
- if (!AcTrViewportView.isDefaultPaperSpaceViewport(entity)) {
2251
- layoutView = this._layoutViewManager.getAt(entity.ownerId);
2252
- if (layoutView) {
2253
- viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this._renderer);
2254
- layoutView.addViewport(viewportView);
2255
- }
2256
- }
2257
- }
2258
- else if (entity instanceof AcDbRasterImage) {
2259
- fileName = entity.imageFileName;
2260
- if (fileName && !entity.image) {
2261
- this._missedImages.set(entity.objectId, fileName);
2262
- }
2263
- }
2264
- return [3 /*break*/, 10];
2265
- case 8:
2266
- error_2 = _a.sent();
2267
- log.error("[AcTrView2d] Failed to convert entity ".concat(entity.objectId, " (").concat(entity.type, "):"), error_2);
2268
- return [3 /*break*/, 10];
2269
- case 9:
2270
- this.decreaseNumOfEntitiesToProcess();
2271
- return [7 /*endfinally*/];
2272
- case 10:
2635
+ _a.label = 3;
2636
+ case 3:
2273
2637
  ++i;
2274
2638
  return [3 /*break*/, 1];
2275
- case 11: return [2 /*return*/];
2639
+ case 4: return [2 /*return*/];
2276
2640
  }
2277
2641
  });
2278
2642
  });
@@ -2297,16 +2661,21 @@ var AcTrView2d = /** @class */ (function (_super) {
2297
2661
  var userData = group.userData;
2298
2662
  userData.spatialIndexChildBoxes = childBoxes;
2299
2663
  };
2300
- AcTrView2d.prototype.handleGroup = function (group, progressive) {
2301
- return __awaiter(this, void 0, void 0, function () {
2664
+ AcTrView2d.prototype.handleGroup = function (group_1, progressive_1) {
2665
+ return __awaiter(this, arguments, void 0, function (group, progressive, epoch) {
2302
2666
  var children, objectsGroupByLayer, children_1, children_1_1, child, renderContext, groupObjectId, groupLayerName, groupChildBoxes, aggregateSpatialBbox;
2303
2667
  var e_11, _a;
2304
2668
  var _this = this;
2669
+ if (epoch === void 0) { epoch = this._convertEpoch; }
2305
2670
  return __generator(this, function (_b) {
2306
2671
  switch (_b.label) {
2307
2672
  case 0: return [4 /*yield*/, this.finishEntityGeometry(group, progressive)];
2308
2673
  case 1:
2309
2674
  _b.sent();
2675
+ if (epoch !== this._convertEpoch) {
2676
+ group.dispose();
2677
+ return [2 /*return*/];
2678
+ }
2310
2679
  this.syncGroupSpatialBoundsForIndexing(group);
2311
2680
  children = group.children;
2312
2681
  objectsGroupByLayer = new Map();
@@ -2410,9 +2779,9 @@ var AcTrView2d = /** @class */ (function (_super) {
2410
2779
  entity.dispose();
2411
2780
  });
2412
2781
  group.dispose();
2413
- if (this._progressiveRendering) {
2414
- this._isDirty = true;
2415
- void this._progressiveOpenFit.afterGeometryBatch(function () {
2782
+ if (progressive) {
2783
+ this.markProgressiveDirty();
2784
+ this._progressiveOpenFit.afterGeometryBatch(function () {
2416
2785
  return _this.resolveLayoutFitBox();
2417
2786
  });
2418
2787
  }
@@ -2427,11 +2796,25 @@ var AcTrView2d = /** @class */ (function (_super) {
2427
2796
  this._numOfEntitiesToProcess = 0;
2428
2797
  log.warn('Something wrong! The number of entities to process should not be less than 0.');
2429
2798
  }
2430
- else if (this._numOfEntitiesToProcess === 0 &&
2431
- !this._progressiveRendering) {
2799
+ else if (this._numOfEntitiesToProcess === 0) {
2800
+ // Always mark dirty when the queue drains. Progressive open throttles
2801
+ // mid-open paints, so the last batch would otherwise never redraw until
2802
+ // the user pans/zooms (animate bails when !_isDirty && !stillLoading).
2432
2803
  this._isDirty = true;
2433
2804
  }
2434
2805
  };
2806
+ /**
2807
+ * Wall-time between cooperative yields during progressive open (ms).
2808
+ * Kept relatively large so convert throughput stays close to the
2809
+ * non-progressive path; smaller budgets made open 2–3× slower.
2810
+ */
2811
+ AcTrView2d.PROGRESSIVE_OPEN_YIELD_BUDGET_MS = 300;
2812
+ /**
2813
+ * Minimum interval between progressive mid-open paints (ms).
2814
+ * Full-scene WebGL paints dominate open wall time on large drawings;
2815
+ * paint much less often than we yield.
2816
+ */
2817
+ AcTrView2d.PROGRESSIVE_OPEN_PAINT_INTERVAL_MS = 1000;
2435
2818
  return AcTrView2d;
2436
2819
  }(AcEdBaseView));
2437
2820
  export { AcTrView2d };