@mlightcad/cad-simple-viewer 1.7.7-preview.1 → 1.7.8-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.
@@ -96,10 +96,10 @@ 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 { AcCmUiYieldGate, AcDbAttribute, AcDbBlockReference, acdbHostApplicationServices, AcDbMText, AcDbRasterImage, AcDbRay, AcDbRenderingCache, AcDbSysVarManager, AcDbText, AcDbViewport, AcDbXline, AcGeBox2d, AcGePoint2d, AcGePoint3d, acgiForegroundColorForBackground, log } from '@mlightcad/data-model';
99
+ import { AcCmUiYieldGate, AcDbAttribute, AcDbBlockReference, acdbHostApplicationServices, AcDbMText, AcDbRasterImage, AcDbRay, AcDbRenderingCache, AcDbSysVarManager, AcDbText, AcDbViewport, AcDbXline, AcGeBox2d, AcGePoint2d, AcGePoint3d, acgiForegroundColorForBackground, AcGiMTextAttachmentPoint, log } from '@mlightcad/data-model';
100
100
  import { AcDbSystemVariables } from '@mlightcad/data-model';
101
101
  import { FontManager } from '@mlightcad/mtext-renderer';
102
- import { AcTrEntity, AcTrGlyphEntity, AcTrGroup, AcTrMTextRenderer, AcTrRenderer, AcTrViewportView, hasPendingComplexLineTypeGlyphs, setAcTrDrawOrderZAllocator } from '@mlightcad/three-renderer';
102
+ import { AcTrEntity, AcTrGlyphEntity, AcTrGroup, AcTrMText, AcTrMTextRenderer, AcTrRenderer, AcTrShape, AcTrViewportView, hasPendingComplexLineTypeGlyphs, setAcTrDrawOrderZAllocator } from '@mlightcad/three-renderer';
103
103
  import { AcTrMatrixUtil } from '@mlightcad/three-renderer';
104
104
  import * as THREE from 'three';
105
105
  import Stats from 'three/examples/jsm/libs/stats.module';
@@ -396,9 +396,17 @@ var AcTrView2d = /** @class */ (function (_super) {
396
396
  /** Convert-phase wall timers (ms) for OPENPERF diagnostics. */
397
397
  _this._convertPhaseMs = {
398
398
  finishGeometry: 0,
399
+ /** Cumulative `asyncDraw` work inside finishEntityGeometry (excludes awaitFonts). */
400
+ finishGeometryAsyncDraw: 0,
401
+ /** Cumulative deferred-queue wait (enqueue → job start). */
402
+ finishGeometryQueueWait: 0,
399
403
  handleGroup: 0,
400
404
  addEntity: 0,
401
- awaitFonts: 0
405
+ awaitFonts: 0,
406
+ glyphEntityCount: 0,
407
+ mtextCount: 0,
408
+ shapeCount: 0,
409
+ sharedGlyphDrawCount: 0
402
410
  };
403
411
  /** Last text-style font preload plan (for OPENPERF). */
404
412
  _this._fontPreloadDiag = null;
@@ -459,6 +467,7 @@ var AcTrView2d = /** @class */ (function (_super) {
459
467
  _this._isDirty = needsRedraw;
460
468
  _this._htmlDirty = false;
461
469
  };
470
+ _this._meshGlyphFlushTimer = null;
462
471
  _this._disposeCanvasTouchCallout = disposeCanvasTouchCallout;
463
472
  var onProgressiveInput = function () { return _this.noteProgressiveUserInput(); };
464
473
  // Pointer and wheel both claim shortcut focus so undo/erase still work
@@ -863,9 +872,15 @@ var AcTrView2d = /** @class */ (function (_super) {
863
872
  ? __assign({}, this._fontPreloadDiag) : undefined });
864
873
  this._convertPhaseMs = {
865
874
  finishGeometry: 0,
875
+ finishGeometryAsyncDraw: 0,
876
+ finishGeometryQueueWait: 0,
866
877
  handleGroup: 0,
867
878
  addEntity: 0,
868
- awaitFonts: 0
879
+ awaitFonts: 0,
880
+ glyphEntityCount: 0,
881
+ mtextCount: 0,
882
+ shapeCount: 0,
883
+ sharedGlyphDrawCount: 0
869
884
  };
870
885
  this._fontPreloadDiag = null;
871
886
  return snapshot;
@@ -1734,6 +1749,10 @@ var AcTrView2d = /** @class */ (function (_super) {
1734
1749
  if (!idle) {
1735
1750
  log.warn('[AcTrView2d] Timed out waiting for deferred geometry before export');
1736
1751
  }
1752
+ // Mesh-glyph InstancedMeshes are coalesced on a 16ms timer after idle.
1753
+ // HTML snapshot walks layer roots and must see those meshes; do not rely
1754
+ // on racing waitUntilIdle against the timer.
1755
+ this.flushMeshGlyphWorldBatch();
1737
1756
  return [2 /*return*/];
1738
1757
  }
1739
1758
  });
@@ -2062,43 +2081,64 @@ var AcTrView2d = /** @class */ (function (_super) {
2062
2081
  };
2063
2082
  AcTrView2d.prototype.editMTextEntity = function (mtext) {
2064
2083
  return __awaiter(this, void 0, void 0, function () {
2065
- var db, editor, applied, result_1, _a, _b;
2084
+ var db, resolvePresentation, presentation, rawLocation, location, direction, attachmentPoint, editorWidth, textHeight, editor, applied, result_1, _a, _b;
2066
2085
  var _c;
2067
- return __generator(this, function (_d) {
2068
- switch (_d.label) {
2086
+ var _d, _e, _f, _g;
2087
+ return __generator(this, function (_h) {
2088
+ switch (_h.label) {
2069
2089
  case 0:
2070
2090
  db = mtext.database;
2071
- if (mtext.lineSpacingFactor !== AcEdMTextEditor.defaultLineSpacingFactor) {
2072
- acapRunDatabaseEdit(db, 'Edit MText', function () {
2073
- var opened = db.openEntityForWrite(mtext);
2074
- if (!(opened instanceof AcDbMText))
2075
- return;
2076
- opened.lineSpacingFactor = AcEdMTextEditor.defaultLineSpacingFactor;
2077
- });
2078
- }
2079
- // Hide the in-scene MTEXT while the inline editor renders its own copy; otherwise
2080
- // both draw at once (double text) when the user double-clicks to edit.
2081
- // this.removeEntity(mtext)
2091
+ // Hide the in-scene MTEXT while the inline editor renders its own copy;
2092
+ // otherwise both draw at once when the user double-clicks to edit.
2093
+ this.setEntitySceneVisible(mtext.objectId, false);
2082
2094
  this._isDirty = true;
2095
+ resolvePresentation = mtext.resolveAnnotativePresentation;
2096
+ presentation = typeof resolvePresentation === 'function' ? resolvePresentation.call(mtext) : null;
2097
+ rawLocation = (_d = presentation === null || presentation === void 0 ? void 0 : presentation.position) !== null && _d !== void 0 ? _d : mtext.location;
2098
+ location = {
2099
+ x: rawLocation.x,
2100
+ y: rawLocation.y,
2101
+ z: (_e = rawLocation.z) !== null && _e !== void 0 ? _e : 0
2102
+ };
2103
+ direction = (_f = presentation === null || presentation === void 0 ? void 0 : presentation.direction) !== null && _f !== void 0 ? _f : mtext.direction;
2104
+ attachmentPoint = (_g = presentation === null || presentation === void 0 ? void 0 : presentation.attachmentPoint) !== null && _g !== void 0 ? _g : mtext.attachmentPoint;
2105
+ editorWidth = presentation && Number.isFinite(presentation.width) && presentation.width > 0
2106
+ ? presentation.width
2107
+ : this.resolveMTextEditorWidth(mtext);
2108
+ textHeight = presentation && Number.isFinite(presentation.height) && presentation.height > 0
2109
+ ? presentation.height
2110
+ : this.resolveMTextEditorTextHeight(mtext);
2083
2111
  editor = new AcEdMTextEditor();
2084
2112
  applied = false;
2085
- _d.label = 1;
2113
+ _h.label = 1;
2086
2114
  case 1:
2087
- _d.trys.push([1, , 4, 5]);
2115
+ _h.trys.push([1, , 4, 5]);
2088
2116
  _b = (_a = editor).open;
2089
2117
  _c = {
2090
2118
  view: this,
2091
- location: mtext.location,
2092
- width: this.resolveMTextEditorWidth(mtext),
2093
- textHeight: this.resolveMTextEditorTextHeight(mtext),
2119
+ location: location,
2120
+ width: editorWidth,
2121
+ textHeight: textHeight,
2094
2122
  initialText: mtext.contents,
2095
- initialAttachmentPoint: mtext.attachmentPoint
2123
+ initialAttachmentPoint: attachmentPoint,
2124
+ initialDrawingDirection: mtext.drawingDirection,
2125
+ rotation: mtext.rotation,
2126
+ directionVector: {
2127
+ x: direction.x,
2128
+ y: direction.y,
2129
+ z: direction.z
2130
+ },
2131
+ // Match the entity's DXF style (group 7), not the drawing's current TEXTSTYLE.
2132
+ textStyleName: mtext.styleName,
2133
+ // Keep DXF group 44 so vertical layout matches the drawing (do not
2134
+ // rewrite to the ribbon default before the editor opens).
2135
+ initialLineSpaceFactor: mtext.lineSpacingFactor
2096
2136
  };
2097
2137
  return [4 /*yield*/, this.getMTextToolbarFontFamilies()];
2098
- case 2: return [4 /*yield*/, _b.apply(_a, [(_c.toolbarFontFamilies = _d.sent(),
2138
+ case 2: return [4 /*yield*/, _b.apply(_a, [(_c.toolbarFontFamilies = _h.sent(),
2099
2139
  _c)])];
2100
2140
  case 3:
2101
- result_1 = _d.sent();
2141
+ result_1 = _h.sent();
2102
2142
  if (!result_1)
2103
2143
  return [2 /*return*/];
2104
2144
  acapRunDatabaseEdit(db, 'Edit MText', function () {
@@ -2115,10 +2155,13 @@ var AcTrView2d = /** @class */ (function (_super) {
2115
2155
  applied = true;
2116
2156
  return [3 /*break*/, 5];
2117
2157
  case 4:
2158
+ // Successful DB edits rebuild the drawable via update listeners; cancel
2159
+ // paths must restore visibility and refresh the original mesh.
2160
+ this.setEntitySceneVisible(mtext.objectId, true);
2118
2161
  if (!applied) {
2119
2162
  this.updateEntity(mtext);
2120
- this._isDirty = true;
2121
2163
  }
2164
+ this._isDirty = true;
2122
2165
  return [7 /*endfinally*/];
2123
2166
  case 5: return [2 /*return*/];
2124
2167
  }
@@ -2134,36 +2177,34 @@ var AcTrView2d = /** @class */ (function (_super) {
2134
2177
  */
2135
2178
  AcTrView2d.prototype.editTextEntity = function (text) {
2136
2179
  return __awaiter(this, void 0, void 0, function () {
2137
- var db, height, textHeight, content, width, editor, applied, result_2, _a, _b;
2138
- var _c;
2180
+ var db, height, textHeight, content, width, rotation, editor, applied, result_2, _a, _b, _c;
2139
2181
  var _d;
2140
- return __generator(this, function (_e) {
2141
- switch (_e.label) {
2182
+ var _e;
2183
+ return __generator(this, function (_f) {
2184
+ switch (_f.label) {
2142
2185
  case 0:
2143
2186
  db = text.database;
2187
+ this.setEntitySceneVisible(text.objectId, false);
2144
2188
  this._isDirty = true;
2145
2189
  height = Number(text.height);
2146
2190
  textHeight = Number.isFinite(height) && height > 0 ? height : this.pixelsToWorldY(24);
2147
- content = (_d = text.textString) !== null && _d !== void 0 ? _d : '';
2191
+ content = (_e = text.textString) !== null && _e !== void 0 ? _e : '';
2148
2192
  width = Math.max(textHeight * Math.max(content.length, 1) * 0.6, textHeight);
2193
+ rotation = Number(text.rotation);
2149
2194
  editor = new AcEdMTextEditor();
2150
2195
  applied = false;
2151
- _e.label = 1;
2196
+ _f.label = 1;
2152
2197
  case 1:
2153
- _e.trys.push([1, , 4, 5]);
2198
+ _f.trys.push([1, , 4, 5]);
2154
2199
  _b = (_a = editor).open;
2155
- _c = {
2156
- view: this,
2157
- location: text.position,
2158
- width: width,
2159
- textHeight: textHeight,
2160
- initialText: content
2161
- };
2200
+ _c = [__assign({ view: this, location: text.position, width: width, textHeight: textHeight, initialText: content,
2201
+ // Single-line TEXT inserts on the baseline by default.
2202
+ initialAttachmentPoint: AcGiMTextAttachmentPoint.BaselineLeft }, (Number.isFinite(rotation) ? { rotation: rotation } : {}))];
2203
+ _d = {};
2162
2204
  return [4 /*yield*/, this.getMTextToolbarFontFamilies()];
2163
- case 2: return [4 /*yield*/, _b.apply(_a, [(_c.toolbarFontFamilies = _e.sent(),
2164
- _c)])];
2205
+ case 2: return [4 /*yield*/, _b.apply(_a, [__assign.apply(void 0, _c.concat([(_d.toolbarFontFamilies = _f.sent(), _d)]))])];
2165
2206
  case 3:
2166
- result_2 = _e.sent();
2207
+ result_2 = _f.sent();
2167
2208
  if (!result_2)
2168
2209
  return [2 /*return*/];
2169
2210
  acapRunDatabaseEdit(db, 'Edit Text', function () {
@@ -2179,10 +2220,11 @@ var AcTrView2d = /** @class */ (function (_super) {
2179
2220
  applied = true;
2180
2221
  return [3 /*break*/, 5];
2181
2222
  case 4:
2223
+ this.setEntitySceneVisible(text.objectId, true);
2182
2224
  if (!applied) {
2183
2225
  this.updateEntity(text);
2184
- this._isDirty = true;
2185
2226
  }
2227
+ this._isDirty = true;
2186
2228
  return [7 /*endfinally*/];
2187
2229
  case 5: return [2 /*return*/];
2188
2230
  }
@@ -2191,9 +2233,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2191
2233
  };
2192
2234
  AcTrView2d.prototype.resolveMTextEditorWidth = function (mtext) {
2193
2235
  var width = Number(mtext.width);
2236
+ // DXF group 41 <= 0 means no defined wrap column (AutoCAD / mtext-renderer).
2237
+ // Pass 0 through so the input box can match renderer no-wrap layout instead
2238
+ // of clamping to a tiny positive width that forces mid-string wrapping.
2194
2239
  if (Number.isFinite(width) && width > 0)
2195
2240
  return width;
2196
- return 1e-4;
2241
+ return 0;
2197
2242
  };
2198
2243
  AcTrView2d.prototype.resolveMTextEditorTextHeight = function (mtext) {
2199
2244
  var textHeight = Number(mtext.height);
@@ -2247,7 +2292,8 @@ var AcTrView2d = /** @class */ (function (_super) {
2247
2292
  * don't have to land pixel-perfect on the viewport line to grab it.
2248
2293
  */
2249
2294
  AcTrView2d.prototype.pick = function (point, hitRadius, pickOneOnly) {
2250
- var e_6, _a;
2295
+ var e_6, _a, e_7, _b;
2296
+ var _c, _d, _e, _f;
2251
2297
  if (point == null)
2252
2298
  point = this.curPos;
2253
2299
  var results = [];
@@ -2269,8 +2315,8 @@ var AcTrView2d = /** @class */ (function (_super) {
2269
2315
  var drillThroughViewportIds = new Set();
2270
2316
  if (isPaperSpace) {
2271
2317
  try {
2272
- for (var _b = __values(activeLayoutView.viewportViews), _c = _b.next(); !_c.done; _c = _b.next()) {
2273
- var vpView = _c.value;
2318
+ for (var _g = __values(activeLayoutView.viewportViews), _h = _g.next(); !_h.done; _h = _g.next()) {
2319
+ var vpView = _h.value;
2274
2320
  if (vpView.containsPaperPoint(point) &&
2275
2321
  !vpView.isNearPaperBorder(point, borderTolerance)) {
2276
2322
  drillThroughViewports.push(vpView);
@@ -2281,7 +2327,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2281
2327
  catch (e_6_1) { e_6 = { error: e_6_1 }; }
2282
2328
  finally {
2283
2329
  try {
2284
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
2330
+ if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
2285
2331
  }
2286
2332
  finally { if (e_6) throw e_6.error; }
2287
2333
  }
@@ -2292,15 +2338,58 @@ var AcTrView2d = /** @class */ (function (_super) {
2292
2338
  // region) and selection feels "stuck on the frame".
2293
2339
  var firstQueryResults = this._scene.search(paperBox);
2294
2340
  var raycaster = activeLayoutView.resetRaycaster(point, threshold);
2341
+ var mtextRenderer = AcTrMTextRenderer.getInstance();
2342
+ var seenPickIds = new Set();
2295
2343
  firstQueryResults.forEach(function (item) {
2296
2344
  if (drillThroughViewportIds.has(item.id))
2297
2345
  return;
2298
- if (!isEffectiveSpatialQueryHit(item))
2346
+ var meshGlyphHit = mtextRenderer.intersectMeshGlyphObjectId(item.id, raycaster);
2347
+ // Hierarchical hits with empty children are normally filtered (stale
2348
+ // coarse root). Mesh-glyph InstancedMeshes are not in child boxes, so
2349
+ // allow the root through when the glyph batch itself is hit.
2350
+ if (!isEffectiveSpatialQueryHit(item) && !meshGlyphHit)
2299
2351
  return;
2300
- if (activeLayout.isIntersectWith(item.id, raycaster)) {
2352
+ if (activeLayout.isIntersectWith(item.id, raycaster) || meshGlyphHit) {
2301
2353
  results.push(item);
2354
+ seenPickIds.add(item.id);
2302
2355
  }
2303
2356
  });
2357
+ // Mesh-glyph text may sit outside the INSERT spatial child union (placements
2358
+ // stolen before child boxes were refreshed). Query the batch pick boxes
2359
+ // directly so clicks on that text still select the entity.
2360
+ var meshGlyphIds = mtextRenderer.queryMeshGlyphObjectIdsIntersectingBox({
2361
+ minX: paperBox.min.x,
2362
+ minY: paperBox.min.y,
2363
+ maxX: paperBox.max.x,
2364
+ maxY: paperBox.max.y
2365
+ });
2366
+ try {
2367
+ for (var meshGlyphIds_1 = __values(meshGlyphIds), meshGlyphIds_1_1 = meshGlyphIds_1.next(); !meshGlyphIds_1_1.done; meshGlyphIds_1_1 = meshGlyphIds_1.next()) {
2368
+ var objectId = meshGlyphIds_1_1.value;
2369
+ if (seenPickIds.has(objectId) || drillThroughViewportIds.has(objectId)) {
2370
+ continue;
2371
+ }
2372
+ if (!mtextRenderer.intersectMeshGlyphObjectId(objectId, raycaster)) {
2373
+ continue;
2374
+ }
2375
+ var pickBox = mtextRenderer.getMeshGlyphWorldPickBoxes().get(objectId);
2376
+ results.push({
2377
+ id: objectId,
2378
+ minX: (_c = pickBox === null || pickBox === void 0 ? void 0 : pickBox.min.x) !== null && _c !== void 0 ? _c : paperBox.min.x,
2379
+ minY: (_d = pickBox === null || pickBox === void 0 ? void 0 : pickBox.min.y) !== null && _d !== void 0 ? _d : paperBox.min.y,
2380
+ maxX: (_e = pickBox === null || pickBox === void 0 ? void 0 : pickBox.max.x) !== null && _e !== void 0 ? _e : paperBox.max.x,
2381
+ maxY: (_f = pickBox === null || pickBox === void 0 ? void 0 : pickBox.max.y) !== null && _f !== void 0 ? _f : paperBox.max.y
2382
+ });
2383
+ seenPickIds.add(objectId);
2384
+ }
2385
+ }
2386
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
2387
+ finally {
2388
+ try {
2389
+ if (meshGlyphIds_1_1 && !meshGlyphIds_1_1.done && (_b = meshGlyphIds_1.return)) _b.call(meshGlyphIds_1);
2390
+ }
2391
+ finally { if (e_7) throw e_7.error; }
2392
+ }
2304
2393
  // 2) For each drill-through viewport, resolve hits against the
2305
2394
  // model-space layout using the viewport's own camera/raycaster.
2306
2395
  if (drillThroughViewports.length > 0) {
@@ -2313,7 +2402,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2313
2402
  * @inheritdoc
2314
2403
  */
2315
2404
  AcTrView2d.prototype.getViewportCoordinateMapper = function (viewportId) {
2316
- var e_7, _a;
2405
+ var e_8, _a;
2317
2406
  var _b, _c;
2318
2407
  var vpView;
2319
2408
  try {
@@ -2325,12 +2414,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2325
2414
  }
2326
2415
  }
2327
2416
  }
2328
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
2417
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
2329
2418
  finally {
2330
2419
  try {
2331
2420
  if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
2332
2421
  }
2333
- finally { if (e_7) throw e_7.error; }
2422
+ finally { if (e_8) throw e_8.error; }
2334
2423
  }
2335
2424
  if (!vpView)
2336
2425
  return undefined;
@@ -2357,7 +2446,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2357
2446
  * paper-space pick.
2358
2447
  */
2359
2448
  AcTrView2d.prototype.pickThroughViewports = function (paperPoint, paperBox, viewports, results) {
2360
- var e_8, _a;
2449
+ var e_9, _a;
2361
2450
  var modelLayout = this._scene.modelSpaceLayout;
2362
2451
  if (!modelLayout)
2363
2452
  return;
@@ -2392,12 +2481,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2392
2481
  _loop_1(vpView);
2393
2482
  }
2394
2483
  }
2395
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
2484
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
2396
2485
  finally {
2397
2486
  try {
2398
2487
  if (viewports_1_1 && !viewports_1_1.done && (_a = viewports_1.return)) _a.call(viewports_1);
2399
2488
  }
2400
- finally { if (e_8) throw e_8.error; }
2489
+ finally { if (e_9) throw e_9.error; }
2401
2490
  }
2402
2491
  };
2403
2492
  /**
@@ -2431,7 +2520,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2431
2520
  * that interior also selects those polylines and can stall highlight work.
2432
2521
  */
2433
2522
  AcTrView2d.prototype.refineCrossingSelectionHits = function (box, results) {
2434
- var e_9, _a;
2523
+ var e_10, _a;
2435
2524
  var _b;
2436
2525
  var database = (_b = AcApDocManager.instance.curDocument) === null || _b === void 0 ? void 0 : _b.database;
2437
2526
  if (!database) {
@@ -2456,12 +2545,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2456
2545
  }
2457
2546
  }
2458
2547
  }
2459
- catch (e_9_1) { e_9 = { error: e_9_1 }; }
2548
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
2460
2549
  finally {
2461
2550
  try {
2462
2551
  if (results_1_1 && !results_1_1.done && (_a = results_1.return)) _a.call(results_1);
2463
2552
  }
2464
- finally { if (e_9) throw e_9.error; }
2553
+ finally { if (e_10) throw e_10.error; }
2465
2554
  }
2466
2555
  return ids;
2467
2556
  };
@@ -2477,7 +2566,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2477
2566
  * @inheritdoc
2478
2567
  */
2479
2568
  AcTrView2d.prototype.updateLayer = function (layer, changes) {
2480
- var e_10, _a;
2569
+ var e_11, _a;
2481
2570
  var touchedObjectIds = this._scene.updateLayer(this.toLayerInfo(layer)).touchedObjectIds;
2482
2571
  if (this._layerAppearance.layerStyleMayHaveChanged(changes)) {
2483
2572
  this._layerAppearance.syncFromLiveRecord(layer);
@@ -2499,12 +2588,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2499
2588
  this.applySessionHiddenObjectState(objectId);
2500
2589
  }
2501
2590
  }
2502
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
2591
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
2503
2592
  finally {
2504
2593
  try {
2505
2594
  if (touchedObjectIds_1_1 && !touchedObjectIds_1_1.done && (_a = touchedObjectIds_1.return)) _a.call(touchedObjectIds_1);
2506
2595
  }
2507
- finally { if (e_10) throw e_10.error; }
2596
+ finally { if (e_11) throw e_11.error; }
2508
2597
  }
2509
2598
  this._isDirty = true;
2510
2599
  };
@@ -2526,6 +2615,10 @@ var AcTrView2d = /** @class */ (function (_super) {
2526
2615
  var threeEntity = this_1.drawEntity(entity_1, true);
2527
2616
  if (threeEntity) {
2528
2617
  threeEntity.objectId = entity_1.objectId;
2618
+ // Grip/jig clones reuse the live entity objectId. Keep mesh glyphs on
2619
+ // the transient tree (merge) so preview cannot enqueue duplicate
2620
+ // world InstancedMesh instances that outlive the transient.
2621
+ threeEntity.userData.skipMeshGlyphInstancing = true;
2529
2622
  void threeEntity
2530
2623
  .asyncDraw()
2531
2624
  .then(function () {
@@ -2741,6 +2834,9 @@ var AcTrView2d = /** @class */ (function (_super) {
2741
2834
  // duplicate progressive slots. updateEntity must release that claim or
2742
2835
  // batchConvert skips the entity (scene already cleared → blank RasterImage).
2743
2836
  this._claimedConvertObjectIds.delete(objectId);
2837
+ // Always drop world mesh-glyph instances first (even if layout remove
2838
+ // fails). Grip preview may have enqueued extras under this objectId.
2839
+ AcTrMTextRenderer.getInstance().removeMeshGlyphInstances(objectId);
2744
2840
  }
2745
2841
  if (this._scene.hasEntity(item.objectId)) {
2746
2842
  this._scene.removeEntity(item.objectId);
@@ -2778,7 +2874,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2778
2874
  * drawable entity that must be rebuilt (for example ATTRIB → INSERT).
2779
2875
  */
2780
2876
  AcTrView2d.prototype.resolveSceneUpdateEntities = function (entities) {
2781
- var e_11, _a;
2877
+ var e_12, _a;
2782
2878
  var _b;
2783
2879
  var db = (_b = AcApDocManager.instance.curDocument) === null || _b === void 0 ? void 0 : _b.database;
2784
2880
  var resolved = [];
@@ -2800,12 +2896,12 @@ var AcTrView2d = /** @class */ (function (_super) {
2800
2896
  resolved.push(target);
2801
2897
  }
2802
2898
  }
2803
- catch (e_11_1) { e_11 = { error: e_11_1 }; }
2899
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
2804
2900
  finally {
2805
2901
  try {
2806
2902
  if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
2807
2903
  }
2808
- finally { if (e_11) throw e_11.error; }
2904
+ finally { if (e_12) throw e_12.error; }
2809
2905
  }
2810
2906
  return resolved;
2811
2907
  };
@@ -2953,6 +3049,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2953
3049
  // the cleared scene nor double-decrements the processing counter.
2954
3050
  this._convertEpoch++;
2955
3051
  this.clearFontLoadedRedrawTimer();
3052
+ this.clearMeshGlyphFlushTimer();
2956
3053
  this._convertQueue.length = 0;
2957
3054
  this._viewportRepairOwnerIds.clear();
2958
3055
  this._numOfEntitiesToProcess = 0;
@@ -2968,6 +3065,8 @@ var AcTrView2d = /** @class */ (function (_super) {
2968
3065
  AcTrView2d.prototype.clear = function () {
2969
3066
  this.abortConversion();
2970
3067
  this._scene.clear();
3068
+ // Scene teardown orphans InstancedMeshes — drop singleton slot maps too.
3069
+ AcTrMTextRenderer.getInstance().resetMeshGlyphBatch();
2971
3070
  this._renderer.resetDrawOrderZ();
2972
3071
  this._isDirty = true;
2973
3072
  this._missedImages.clear();
@@ -2986,6 +3085,7 @@ var AcTrView2d = /** @class */ (function (_super) {
2986
3085
  * @returns Parked state owned by {@link AcApDocSession} until restore.
2987
3086
  */
2988
3087
  AcTrView2d.prototype.captureSessionState = function () {
3088
+ this.clearMeshGlyphFlushTimer();
2989
3089
  return {
2990
3090
  scene: this._scene,
2991
3091
  layoutViewManager: this._layoutViewManager,
@@ -2995,7 +3095,9 @@ var AcTrView2d = /** @class */ (function (_super) {
2995
3095
  loadingLayouts: this._loadingLayouts,
2996
3096
  missedImages: this._missedImages,
2997
3097
  missedFonts: this._renderer.snapshotMissedFonts(),
2998
- selectionIds: this.selectionSet.ids
3098
+ selectionIds: this.selectionSet.ids,
3099
+ // Detach so the next document cannot pick/highlight this one's glyphs.
3100
+ meshGlyphBatch: AcTrMTextRenderer.getInstance().detachMeshGlyphBatch()
2999
3101
  };
3000
3102
  };
3001
3103
  /**
@@ -3007,6 +3109,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3007
3109
  var _a, _b;
3008
3110
  this._convertEpoch++;
3009
3111
  this.clearFontLoadedRedrawTimer();
3112
+ this.clearMeshGlyphFlushTimer();
3010
3113
  this.cancelOpenLineworkFrame();
3011
3114
  this._convertQueue.length = 0;
3012
3115
  this._viewportRepairOwnerIds.clear();
@@ -3023,6 +3126,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3023
3126
  this._loadingLayouts = state.loadingLayouts;
3024
3127
  this._missedImages = state.missedImages;
3025
3128
  this._renderer.replaceMissedFonts((_b = state.missedFonts) !== null && _b !== void 0 ? _b : {});
3129
+ AcTrMTextRenderer.getInstance().attachMeshGlyphBatch(state.meshGlyphBatch);
3026
3130
  this.rebindLayerAppearance();
3027
3131
  this._layoutViewManager.resize(this.width, this.height);
3028
3132
  this.selectionSet.clear();
@@ -3069,6 +3173,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3069
3173
  * @param state - Parked snapshot to discard.
3070
3174
  */
3071
3175
  AcTrView2d.prototype.disposeSessionState = function (state) {
3176
+ state.meshGlyphBatch.dispose();
3072
3177
  state.scene.clear();
3073
3178
  state.layoutViewManager = new AcTrLayoutViewManager();
3074
3179
  state.initializedLayouts.clear();
@@ -3581,7 +3686,7 @@ var AcTrView2d = /** @class */ (function (_super) {
3581
3686
  * @param layoutBtrId Input the block table record id of the layout
3582
3687
  */
3583
3688
  AcTrView2d.prototype.loadLayoutEntitiesIfNeeded = function (layoutBtrId) {
3584
- var e_12, _a;
3689
+ var e_13, _a;
3585
3690
  var _this = this;
3586
3691
  try {
3587
3692
  var db = AcApDocManager.instance.curDocument.database;
@@ -3645,12 +3750,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3645
3750
  entities_2.push(entity);
3646
3751
  }
3647
3752
  }
3648
- catch (e_12_1) { e_12 = { error: e_12_1 }; }
3753
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
3649
3754
  finally {
3650
3755
  try {
3651
3756
  if (iterator_1_1 && !iterator_1_1.done && (_a = iterator_1.return)) _a.call(iterator_1);
3652
3757
  }
3653
- finally { if (e_12) throw e_12.error; }
3758
+ finally { if (e_13) throw e_13.error; }
3654
3759
  }
3655
3760
  if (entities_2.length === 0) {
3656
3761
  // Empty layout (e.g. a freshly-created paper space tab). Mark as
@@ -3877,7 +3982,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3877
3982
  */
3878
3983
  AcTrView2d.prototype.finishEntityGeometry = function (threeEntity, _progressive) {
3879
3984
  return __awaiter(this, void 0, void 0, function () {
3880
- var t0, isGroup, groupPending, needsGlyphDraw;
3985
+ var t0, isGroup, groupPending, needsGlyphDraw, noteGlyphCounts, td_1, td_2, td;
3986
+ var _this = this;
3881
3987
  return __generator(this, function (_a) {
3882
3988
  switch (_a.label) {
3883
3989
  case 0:
@@ -3897,29 +4003,47 @@ var AcTrView2d = /** @class */ (function (_super) {
3897
4003
  _a.sent();
3898
4004
  _a.label = 3;
3899
4005
  case 3:
4006
+ noteGlyphCounts = function () {
4007
+ _this._convertPhaseMs.glyphEntityCount++;
4008
+ if (threeEntity instanceof AcTrMText) {
4009
+ _this._convertPhaseMs.mtextCount++;
4010
+ }
4011
+ else if (threeEntity instanceof AcTrShape) {
4012
+ _this._convertPhaseMs.shapeCount++;
4013
+ }
4014
+ };
3900
4015
  if (!isGroup) return [3 /*break*/, 5];
3901
4016
  // Linework-only INSERTs (no empty glyph shells) skip asyncDraw; spatial
3902
4017
  // boxes are refreshed by syncGroupSpatialBoundsForIndexing after commit.
3903
4018
  if (!groupPending) {
3904
4019
  return [2 /*return*/];
3905
4020
  }
4021
+ noteGlyphCounts();
4022
+ td_1 = performance.now();
3906
4023
  return [4 /*yield*/, threeEntity.asyncDraw()];
3907
4024
  case 4:
3908
4025
  _a.sent();
4026
+ this._convertPhaseMs.finishGeometryAsyncDraw += performance.now() - td_1;
3909
4027
  return [2 /*return*/];
3910
4028
  case 5:
3911
4029
  if (!hasPendingComplexLineTypeGlyphs(threeEntity)) return [3 /*break*/, 7];
4030
+ noteGlyphCounts();
4031
+ td_2 = performance.now();
3912
4032
  return [4 /*yield*/, threeEntity.asyncDraw()];
3913
4033
  case 6:
3914
4034
  _a.sent();
4035
+ this._convertPhaseMs.finishGeometryAsyncDraw += performance.now() - td_2;
3915
4036
  return [2 /*return*/];
3916
4037
  case 7:
3917
4038
  if (threeEntity.hasDrawableGeometry()) {
3918
4039
  return [2 /*return*/];
3919
4040
  }
4041
+ noteGlyphCounts();
4042
+ td = performance.now();
3920
4043
  return [4 /*yield*/, threeEntity.asyncDraw()];
3921
4044
  case 8:
3922
4045
  _a.sent();
4046
+ this._convertPhaseMs.finishGeometryAsyncDraw += performance.now() - td;
3923
4047
  return [3 /*break*/, 10];
3924
4048
  case 9:
3925
4049
  this._convertPhaseMs.finishGeometry += performance.now() - t0;
@@ -3974,8 +4098,8 @@ var AcTrView2d = /** @class */ (function (_super) {
3974
4098
  */
3975
4099
  AcTrView2d.prototype.redrawGlyphEntitiesAfterFontLoad = function (epoch, fontName) {
3976
4100
  return __awaiter(this, void 0, void 0, function () {
3977
- var redrawEpoch, glyphs, _a, _b, layout, glyphs_1, glyphs_1_1, glyph, e_13_1, defaults, normalized_1, msSinceIdle, db;
3978
- var e_14, _c, e_13, _d;
4101
+ var redrawEpoch, glyphs, _a, _b, layout, glyphs_1, glyphs_1_1, glyph, e_14_1, defaults, normalized_1, msSinceIdle, db;
4102
+ var e_15, _c, e_14, _d;
3979
4103
  var _e, _f;
3980
4104
  return __generator(this, function (_g) {
3981
4105
  switch (_g.label) {
@@ -3997,12 +4121,12 @@ var AcTrView2d = /** @class */ (function (_super) {
3997
4121
  });
3998
4122
  }
3999
4123
  }
4000
- catch (e_14_1) { e_14 = { error: e_14_1 }; }
4124
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
4001
4125
  finally {
4002
4126
  try {
4003
4127
  if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
4004
4128
  }
4005
- finally { if (e_14) throw e_14.error; }
4129
+ finally { if (e_15) throw e_15.error; }
4006
4130
  }
4007
4131
  _g.label = 1;
4008
4132
  case 1:
@@ -4025,14 +4149,14 @@ var AcTrView2d = /** @class */ (function (_super) {
4025
4149
  return [3 /*break*/, 2];
4026
4150
  case 5: return [3 /*break*/, 8];
4027
4151
  case 6:
4028
- e_13_1 = _g.sent();
4029
- e_13 = { error: e_13_1 };
4152
+ e_14_1 = _g.sent();
4153
+ e_14 = { error: e_14_1 };
4030
4154
  return [3 /*break*/, 8];
4031
4155
  case 7:
4032
4156
  try {
4033
4157
  if (glyphs_1_1 && !glyphs_1_1.done && (_d = glyphs_1.return)) _d.call(glyphs_1);
4034
4158
  }
4035
- finally { if (e_13) throw e_13.error; }
4159
+ finally { if (e_14) throw e_14.error; }
4036
4160
  return [7 /*endfinally*/];
4037
4161
  case 8:
4038
4162
  if (glyphs.length > 0) {
@@ -4083,7 +4207,7 @@ var AcTrView2d = /** @class */ (function (_super) {
4083
4207
  });
4084
4208
  };
4085
4209
  AcTrView2d.prototype.groupHasPendingGlyphGeometry = function (group) {
4086
- var e_15, _a;
4210
+ var e_16, _a;
4087
4211
  try {
4088
4212
  // Prefer O(block-def shells). A full THREE.traverse over a mega-block
4089
4213
  // (100k+ flattened mesh leaves) was dominating scene-convert wall time.
@@ -4094,12 +4218,12 @@ var AcTrView2d = /** @class */ (function (_super) {
4094
4218
  }
4095
4219
  }
4096
4220
  }
4097
- catch (e_15_1) { e_15 = { error: e_15_1 }; }
4221
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
4098
4222
  finally {
4099
4223
  try {
4100
4224
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
4101
4225
  }
4102
- finally { if (e_15) throw e_15.error; }
4226
+ finally { if (e_16) throw e_16.error; }
4103
4227
  }
4104
4228
  return AcTrView2d.directChildrenHavePendingGlyphs(group);
4105
4229
  };
@@ -4110,7 +4234,7 @@ var AcTrView2d = /** @class */ (function (_super) {
4110
4234
  * a whole-floor INSERT is O(glyph shells), not O(all drawable leaves).
4111
4235
  */
4112
4236
  AcTrView2d.directChildrenHavePendingGlyphs = function (root) {
4113
- var e_16, _a;
4237
+ var e_17, _a;
4114
4238
  var children = root.children;
4115
4239
  for (var i = 0; i < children.length; i++) {
4116
4240
  var child = children[i];
@@ -4122,7 +4246,7 @@ var AcTrView2d = /** @class */ (function (_super) {
4122
4246
  }
4123
4247
  if (child instanceof AcTrGroup) {
4124
4248
  try {
4125
- for (var _b = (e_16 = void 0, __values(child.getSourceEntities())), _c = _b.next(); !_c.done; _c = _b.next()) {
4249
+ for (var _b = (e_17 = void 0, __values(child.getSourceEntities())), _c = _b.next(); !_c.done; _c = _b.next()) {
4126
4250
  var entity = _c.value;
4127
4251
  if (entity instanceof AcTrGlyphEntity &&
4128
4252
  !entity.hasDrawableGeometry()) {
@@ -4130,12 +4254,12 @@ var AcTrView2d = /** @class */ (function (_super) {
4130
4254
  }
4131
4255
  }
4132
4256
  }
4133
- catch (e_16_1) { e_16 = { error: e_16_1 }; }
4257
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
4134
4258
  finally {
4135
4259
  try {
4136
4260
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
4137
4261
  }
4138
- finally { if (e_16) throw e_16.error; }
4262
+ finally { if (e_17) throw e_17.error; }
4139
4263
  }
4140
4264
  if (AcTrView2d.directChildrenHavePendingGlyphs(child)) {
4141
4265
  return true;
@@ -4164,7 +4288,11 @@ var AcTrView2d = /** @class */ (function (_super) {
4164
4288
  return;
4165
4289
  }
4166
4290
  this._pendingGeometryJobs++;
4167
- this._deferredGeometryQueue.push({ run: run, epoch: epoch });
4291
+ this._deferredGeometryQueue.push({
4292
+ run: run,
4293
+ epoch: epoch,
4294
+ enqueuedAt: performance.now()
4295
+ });
4168
4296
  this.pumpDeferredGeometryQueue();
4169
4297
  };
4170
4298
  /**
@@ -4179,6 +4307,8 @@ var AcTrView2d = /** @class */ (function (_super) {
4179
4307
  this_2._pendingGeometryJobs = Math.max(0, this_2._pendingGeometryJobs - 1);
4180
4308
  return "continue";
4181
4309
  }
4310
+ this_2._convertPhaseMs.finishGeometryQueueWait +=
4311
+ performance.now() - job.enqueuedAt;
4182
4312
  this_2._deferredGeometryActive++;
4183
4313
  void job
4184
4314
  .run()
@@ -4293,7 +4423,7 @@ var AcTrView2d = /** @class */ (function (_super) {
4293
4423
  * paper layouts.
4294
4424
  */
4295
4425
  AcTrView2d.prototype.ensureViewportViews = function (blockTableRecord, layoutView) {
4296
- var e_17, _a;
4426
+ var e_18, _a;
4297
4427
  var iterator = blockTableRecord.newIterator();
4298
4428
  try {
4299
4429
  for (var iterator_2 = __values(iterator), iterator_2_1 = iterator_2.next(); !iterator_2_1.done; iterator_2_1 = iterator_2.next()) {
@@ -4306,12 +4436,12 @@ var AcTrView2d = /** @class */ (function (_super) {
4306
4436
  layoutView.addViewport(viewportView);
4307
4437
  }
4308
4438
  }
4309
- catch (e_17_1) { e_17 = { error: e_17_1 }; }
4439
+ catch (e_18_1) { e_18 = { error: e_18_1 }; }
4310
4440
  finally {
4311
4441
  try {
4312
4442
  if (iterator_2_1 && !iterator_2_1.done && (_a = iterator_2.return)) _a.call(iterator_2);
4313
4443
  }
4314
- finally { if (e_17) throw e_17.error; }
4444
+ finally { if (e_18) throw e_18.error; }
4315
4445
  }
4316
4446
  };
4317
4447
  /**
@@ -4320,7 +4450,7 @@ var AcTrView2d = /** @class */ (function (_super) {
4320
4450
  * {@link AcDbViewport.frozenLayerIds} but omit them from `toGiViewport()`.
4321
4451
  */
4322
4452
  AcTrView2d.prototype.toGiViewportWithFrozenLayers = function (entity) {
4323
- var e_18, _a;
4453
+ var e_19, _a;
4324
4454
  var _b;
4325
4455
  var gi = entity.toGiViewport();
4326
4456
  if (gi.frozenLayers && gi.frozenLayers.length > 0) {
@@ -4340,12 +4470,12 @@ var AcTrView2d = /** @class */ (function (_super) {
4340
4470
  names.push(layer.name);
4341
4471
  }
4342
4472
  }
4343
- catch (e_18_1) { e_18 = { error: e_18_1 }; }
4473
+ catch (e_19_1) { e_19 = { error: e_19_1 }; }
4344
4474
  finally {
4345
4475
  try {
4346
4476
  if (ids_1_1 && !ids_1_1.done && (_a = ids_1.return)) _a.call(ids_1);
4347
4477
  }
4348
- finally { if (e_18) throw e_18.error; }
4478
+ finally { if (e_19) throw e_19.error; }
4349
4479
  }
4350
4480
  }
4351
4481
  gi.frozenLayerIds = __spreadArray([], __read(ids), false);
@@ -4665,8 +4795,8 @@ var AcTrView2d = /** @class */ (function (_super) {
4665
4795
  };
4666
4796
  AcTrView2d.prototype.handleGroup = function (group_1, progressive_1) {
4667
4797
  return __awaiter(this, arguments, void 0, function (group, progressive, epoch, convertGen) {
4668
- 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_19_1;
4669
- var e_20, _b, e_19, _c;
4798
+ 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_20_1;
4799
+ var e_21, _b, e_20, _c;
4670
4800
  var _this = this;
4671
4801
  var _d, _e;
4672
4802
  if (epoch === void 0) { epoch = this._convertEpoch; }
@@ -4721,12 +4851,12 @@ var AcTrView2d = /** @class */ (function (_super) {
4721
4851
  child.parent = null;
4722
4852
  }
4723
4853
  }
4724
- catch (e_20_1) { e_20 = { error: e_20_1 }; }
4854
+ catch (e_21_1) { e_21 = { error: e_21_1 }; }
4725
4855
  finally {
4726
4856
  try {
4727
4857
  if (children_1_1 && !children_1_1.done && (_b = children_1.return)) _b.call(children_1);
4728
4858
  }
4729
- finally { if (e_20) throw e_20.error; }
4859
+ finally { if (e_21) throw e_21.error; }
4730
4860
  }
4731
4861
  renderContext = group.renderContext;
4732
4862
  groupObjectId = group.objectId;
@@ -4841,14 +4971,14 @@ var AcTrView2d = /** @class */ (function (_super) {
4841
4971
  return [3 /*break*/, 4];
4842
4972
  case 7: return [3 /*break*/, 10];
4843
4973
  case 8:
4844
- e_19_1 = _f.sent();
4845
- e_19 = { error: e_19_1 };
4974
+ e_20_1 = _f.sent();
4975
+ e_20 = { error: e_20_1 };
4846
4976
  return [3 /*break*/, 10];
4847
4977
  case 9:
4848
4978
  try {
4849
4979
  if (objectsGroupByLayer_1_1 && !objectsGroupByLayer_1_1.done && (_c = objectsGroupByLayer_1.return)) _c.call(objectsGroupByLayer_1);
4850
4980
  }
4851
- finally { if (e_19) throw e_19.error; }
4981
+ finally { if (e_20) throw e_20.error; }
4852
4982
  return [7 /*endfinally*/];
4853
4983
  case 10:
4854
4984
  group.dispose();
@@ -4912,6 +5042,9 @@ var AcTrView2d = /** @class */ (function (_super) {
4912
5042
  AcTrView2d.prototype.stampEntityProcessingIdle = function () {
4913
5043
  if (this._numOfEntitiesToProcess === 0 && this._pendingGeometryJobs === 0) {
4914
5044
  this._entityProcessingIdleAt = performance.now();
5045
+ // Coalesce: deferred waves can briefly hit 0 pending before the next
5046
+ // enqueue; delay flush so one open prefers a single InstancedMesh build.
5047
+ this.scheduleMeshGlyphWorldBatchFlush();
4915
5048
  // Release block templates after INSERT convert/glyph work finishes.
4916
5049
  // Clearing at converter END was too early (convert still draining);
4917
5050
  // keeping templates until the next open kept baked shells in memory and
@@ -4919,6 +5052,85 @@ var AcTrView2d = /** @class */ (function (_super) {
4919
5052
  AcDbRenderingCache.instance.clear();
4920
5053
  }
4921
5054
  };
5055
+ AcTrView2d.prototype.clearMeshGlyphFlushTimer = function () {
5056
+ if (this._meshGlyphFlushTimer != null) {
5057
+ clearTimeout(this._meshGlyphFlushTimer);
5058
+ this._meshGlyphFlushTimer = null;
5059
+ }
5060
+ };
5061
+ /**
5062
+ * Schedules {@link flushMeshGlyphWorldBatch} on the next macrotask if the
5063
+ * convert/glyph pools are still idle.
5064
+ */
5065
+ AcTrView2d.prototype.scheduleMeshGlyphWorldBatchFlush = function () {
5066
+ var _this = this;
5067
+ var mtextRenderer = AcTrMTextRenderer.getInstance();
5068
+ if (!mtextRenderer.getMeshGlyphInstancing()) {
5069
+ return;
5070
+ }
5071
+ this.clearMeshGlyphFlushTimer();
5072
+ var epoch = this._convertEpoch;
5073
+ this._meshGlyphFlushTimer = setTimeout(function () {
5074
+ _this._meshGlyphFlushTimer = null;
5075
+ if (epoch !== _this._convertEpoch) {
5076
+ return;
5077
+ }
5078
+ if (_this._numOfEntitiesToProcess > 0 || _this._pendingGeometryJobs > 0) {
5079
+ return;
5080
+ }
5081
+ _this.flushMeshGlyphWorldBatch();
5082
+ }, 16);
5083
+ };
5084
+ /**
5085
+ * Builds scene-level mesh-glyph InstancedMeshes after deferred asyncDraw
5086
+ * drains. No-op unless {@link AcTrMTextRenderer.setMeshGlyphInstancing}.
5087
+ *
5088
+ * Cancels any pending coalesce timer so callers (HTML export) can flush
5089
+ * synchronously without waiting for the 16ms delay.
5090
+ */
5091
+ AcTrView2d.prototype.flushMeshGlyphWorldBatch = function () {
5092
+ var _this = this;
5093
+ this.clearMeshGlyphFlushTimer();
5094
+ var mtextRenderer = AcTrMTextRenderer.getInstance();
5095
+ if (!mtextRenderer.getMeshGlyphInstancing()) {
5096
+ return;
5097
+ }
5098
+ var _a = mtextRenderer.flushMeshGlyphInstances(this._scene.internalScene, function (layerName) { return _this.resolveMeshGlyphLayerParent(layerName); }), placements = _a.placements, buckets = _a.buckets;
5099
+ if (placements > 0) {
5100
+ // Prefix for headless CLI console forwarding.
5101
+ console.log("[cad-simple-viewer-cli] mesh glyph InstancedMesh flush: placements=".concat(placements, " buckets=").concat(buckets));
5102
+ }
5103
+ };
5104
+ /**
5105
+ * Prefers the active layout's layer group, then any layout that already has
5106
+ * that layer (multi-layout HTML export). Falls back to null so flush can
5107
+ * parent under the bookkeeping root.
5108
+ */
5109
+ AcTrView2d.prototype.resolveMeshGlyphLayerParent = function (layerName) {
5110
+ var e_22, _a;
5111
+ var _b;
5112
+ var active = (_b = this._scene.activeLayout) === null || _b === void 0 ? void 0 : _b.getLayer(layerName);
5113
+ if (active) {
5114
+ return active.internalObject;
5115
+ }
5116
+ try {
5117
+ for (var _c = __values(this._scene.layouts.values()), _d = _c.next(); !_d.done; _d = _c.next()) {
5118
+ var layout = _d.value;
5119
+ var layer = layout.getLayer(layerName);
5120
+ if (layer) {
5121
+ return layer.internalObject;
5122
+ }
5123
+ }
5124
+ }
5125
+ catch (e_22_1) { e_22 = { error: e_22_1 }; }
5126
+ finally {
5127
+ try {
5128
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
5129
+ }
5130
+ finally { if (e_22) throw e_22.error; }
5131
+ }
5132
+ return null;
5133
+ };
4922
5134
  /**
4923
5135
  * Wall-time between cooperative yields during scene convert (ms) when
4924
5136
  * progressive rendering is off. Overlay still blocks input, so a larger