pptx-angular-viewer 3.21.0 → 4.1.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ A release listed with no entries carried no Conventional Commit in this package'
7
7
  scope: scripts/release-plan.mjs re-releases a package whenever any of its files
8
8
  change, not only on conventional ones.
9
9
 
10
+ ## [4.0.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@4.0.0) - 2026-09-18
11
+
12
+ ### Bug Fixes
13
+
14
+ - **core:** Switch to emf-converter's unified convertMetafileToDataUrl (by @ChristopherVR) ([70a2ea5](https://github.com/ChristopherVR/pptx-viewer/commit/70a2ea5059af31627dfa1f0a7ebe5e12219804bb))
15
+
16
+ ## [3.21.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@3.21.0) - 2026-09-17
17
+
18
+ ### Features
19
+
20
+ - **shared:** Support host-owned collaboration sessions (by @Sudhansh6) ([cd44eb3](https://github.com/ChristopherVR/pptx-viewer/commit/cd44eb3e79ff6d23e9b92d6f9b0a4eb4ea4f228e))
21
+ - **angular:** Attach host-owned collaboration sessions (by @Sudhansh6) ([802d2b0](https://github.com/ChristopherVR/pptx-viewer/commit/802d2b0186b1406ff45978f0b12e305aa8d217ce))
22
+
23
+ ### Bug Fixes
24
+
25
+ - **shared:** Preserve consecutive collaborative text breaks (by @Sudhansh6) ([10d1667](https://github.com/ChristopherVR/pptx-viewer/commit/10d1667b3b41cf650985324b2accf7b006bf53c1))
26
+ - **shared:** Unify host session readiness and writeback (by @Sudhansh6) ([4635d01](https://github.com/ChristopherVR/pptx-viewer/commit/4635d01dd9f0d6cd48cfbd3f1ad9968acfd02906))
27
+ - **angular:** Share host readiness and seed ready creator rooms (by @Sudhansh6) ([9e6b718](https://github.com/ChristopherVR/pptx-viewer/commit/9e6b7187861162fe145f6f10f79f0979a9cf042a))
28
+
10
29
  ## [3.20.1](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@3.20.1) - 2026-09-17
11
30
 
12
31
  ### Bug Fixes
@@ -1,4 +1,4 @@
1
- import { t as toChatSummary } from './pptx-angular-viewer-pptx-angular-viewer-vihD7wHf.mjs';
1
+ import { t as toChatSummary } from './pptx-angular-viewer-pptx-angular-viewer-CWKJaKd3.mjs';
2
2
 
3
3
  /**
4
4
  * IndexedDB backend for {@link createChatHistoryStore}. Kept in its own module
@@ -103,4 +103,4 @@ function createIdbBackend(db) {
103
103
  }
104
104
 
105
105
  export { CHAT_STORE, createIdbBackend, openChatDb };
106
- //# sourceMappingURL=pptx-angular-viewer-chat-history-idb-DewzErjQ.mjs.map
106
+ //# sourceMappingURL=pptx-angular-viewer-chat-history-idb-OhxbpmW3.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pptx-angular-viewer-chat-history-idb-DewzErjQ.mjs","sources":["../../src/internal/shared-src/ai/chat-history-idb.ts"],"sourcesContent":["/**\n * IndexedDB backend for {@link createChatHistoryStore}. Kept in its own module\n * so the localStorage fallback and the public factory stay within the per-file\n * size budget. Uses the raw IndexedDB API with tiny promise wrappers so no new\n * runtime dependency is pulled in.\n *\n * Every browser global is touched lazily from inside a function, never at module\n * load, so importing this file in a non-DOM environment (Node, SSR) never throws.\n */\n\nimport type { ChatBackend, PptxAiChatSummary, PptxAiStoredChat } from './chat-history-store';\nimport { toChatSummary } from './chat-history-store';\n\n/** Object store holding one record per chat, keyed by `id`. */\nexport const CHAT_STORE = 'chats';\n/** Index over the optional `deckId` key path, used for per-deck filtering. */\nconst DECK_INDEX = 'deckId';\n\n/** Resolve an `IDBRequest` to a promise. */\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB request failed.'));\n\t});\n}\n\n/** Resolve when a transaction commits (or reject on error/abort). */\nfunction transactionDone(tx: IDBTransaction): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\ttx.oncomplete = () => resolve();\n\t\ttx.onerror = () => reject(tx.error ?? new Error('IndexedDB transaction failed.'));\n\t\ttx.onabort = () => reject(tx.error ?? new Error('IndexedDB transaction aborted.'));\n\t});\n}\n\n/**\n * Open (and if needed create/upgrade) the chat database. Throws when there is no\n * `indexedDB` global so the caller can transparently fall back to localStorage.\n */\nexport function openChatDb(dbName: string): Promise<IDBDatabase> {\n\tconst idb = typeof indexedDB !== 'undefined' ? indexedDB : undefined;\n\tif (!idb) {\n\t\tthrow new Error('IndexedDB is not available in this environment.');\n\t}\n\treturn new Promise<IDBDatabase>((resolve, reject) => {\n\t\tconst open = idb.open(dbName, 1);\n\t\topen.onupgradeneeded = () => {\n\t\t\tconst db = open.result;\n\t\t\tif (!db.objectStoreNames.contains(CHAT_STORE)) {\n\t\t\t\tconst store = db.createObjectStore(CHAT_STORE, { keyPath: 'id' });\n\t\t\t\tstore.createIndex(DECK_INDEX, 'deckId', { unique: false });\n\t\t\t}\n\t\t};\n\t\topen.onsuccess = () => resolve(open.result);\n\t\topen.onerror = () => reject(open.error ?? new Error('Failed to open IndexedDB.'));\n\t\topen.onblocked = () => reject(new Error('IndexedDB open blocked by another connection.'));\n\t});\n}\n\n/** Read every stored chat (newest-first ordering is applied by the caller). */\nasync function readAll(db: IDBDatabase, deckId: string | undefined): Promise<PptxAiStoredChat[]> {\n\tconst tx = db.transaction(CHAT_STORE, 'readonly');\n\tconst store = tx.objectStore(CHAT_STORE);\n\tconst source =\n\t\tdeckId === undefined\n\t\t\t? store.getAll()\n\t\t\t: store.index(DECK_INDEX).getAll(IDBKeyRange.only(deckId));\n\tconst rows = await requestToPromise<PptxAiStoredChat[]>(source);\n\tawait transactionDone(tx);\n\treturn rows;\n}\n\n/** Create an IndexedDB-backed {@link ChatBackend}. */\nexport function createIdbBackend(db: IDBDatabase): ChatBackend {\n\treturn {\n\t\tasync list(deckId: string | undefined): Promise<PptxAiChatSummary[]> {\n\t\t\tconst rows = await readAll(db, deckId);\n\t\t\treturn rows.map(toChatSummary).sort((a, b) => b.updatedAt - a.updatedAt);\n\t\t},\n\t\tasync load(id: string): Promise<PptxAiStoredChat | null> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readonly');\n\t\t\tconst row = await requestToPromise<PptxAiStoredChat | undefined>(\n\t\t\t\ttx.objectStore(CHAT_STORE).get(id),\n\t\t\t);\n\t\t\tawait transactionDone(tx);\n\t\t\treturn row ?? null;\n\t\t},\n\t\tasync save(chat: PptxAiStoredChat): Promise<void> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readwrite');\n\t\t\ttx.objectStore(CHAT_STORE).put(chat);\n\t\t\tawait transactionDone(tx);\n\t\t},\n\t\tasync remove(id: string): Promise<void> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readwrite');\n\t\t\ttx.objectStore(CHAT_STORE).delete(id);\n\t\t\tawait transactionDone(tx);\n\t\t},\n\t\tasync clear(deckId: string | undefined): Promise<void> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readwrite');\n\t\t\tconst store = tx.objectStore(CHAT_STORE);\n\t\t\tif (deckId === undefined) {\n\t\t\t\tstore.clear();\n\t\t\t} else {\n\t\t\t\tconst ids = await requestToPromise<IDBValidKey[]>(\n\t\t\t\t\tstore.index(DECK_INDEX).getAllKeys(IDBKeyRange.only(deckId)),\n\t\t\t\t);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tstore.delete(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait transactionDone(tx);\n\t\t},\n\t};\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pptx-angular-viewer-chat-history-idb-OhxbpmW3.mjs","sources":["../../src/internal/shared-src/ai/chat-history-idb.ts"],"sourcesContent":["/**\n * IndexedDB backend for {@link createChatHistoryStore}. Kept in its own module\n * so the localStorage fallback and the public factory stay within the per-file\n * size budget. Uses the raw IndexedDB API with tiny promise wrappers so no new\n * runtime dependency is pulled in.\n *\n * Every browser global is touched lazily from inside a function, never at module\n * load, so importing this file in a non-DOM environment (Node, SSR) never throws.\n */\n\nimport type { ChatBackend, PptxAiChatSummary, PptxAiStoredChat } from './chat-history-store';\nimport { toChatSummary } from './chat-history-store';\n\n/** Object store holding one record per chat, keyed by `id`. */\nexport const CHAT_STORE = 'chats';\n/** Index over the optional `deckId` key path, used for per-deck filtering. */\nconst DECK_INDEX = 'deckId';\n\n/** Resolve an `IDBRequest` to a promise. */\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB request failed.'));\n\t});\n}\n\n/** Resolve when a transaction commits (or reject on error/abort). */\nfunction transactionDone(tx: IDBTransaction): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\ttx.oncomplete = () => resolve();\n\t\ttx.onerror = () => reject(tx.error ?? new Error('IndexedDB transaction failed.'));\n\t\ttx.onabort = () => reject(tx.error ?? new Error('IndexedDB transaction aborted.'));\n\t});\n}\n\n/**\n * Open (and if needed create/upgrade) the chat database. Throws when there is no\n * `indexedDB` global so the caller can transparently fall back to localStorage.\n */\nexport function openChatDb(dbName: string): Promise<IDBDatabase> {\n\tconst idb = typeof indexedDB !== 'undefined' ? indexedDB : undefined;\n\tif (!idb) {\n\t\tthrow new Error('IndexedDB is not available in this environment.');\n\t}\n\treturn new Promise<IDBDatabase>((resolve, reject) => {\n\t\tconst open = idb.open(dbName, 1);\n\t\topen.onupgradeneeded = () => {\n\t\t\tconst db = open.result;\n\t\t\tif (!db.objectStoreNames.contains(CHAT_STORE)) {\n\t\t\t\tconst store = db.createObjectStore(CHAT_STORE, { keyPath: 'id' });\n\t\t\t\tstore.createIndex(DECK_INDEX, 'deckId', { unique: false });\n\t\t\t}\n\t\t};\n\t\topen.onsuccess = () => resolve(open.result);\n\t\topen.onerror = () => reject(open.error ?? new Error('Failed to open IndexedDB.'));\n\t\topen.onblocked = () => reject(new Error('IndexedDB open blocked by another connection.'));\n\t});\n}\n\n/** Read every stored chat (newest-first ordering is applied by the caller). */\nasync function readAll(db: IDBDatabase, deckId: string | undefined): Promise<PptxAiStoredChat[]> {\n\tconst tx = db.transaction(CHAT_STORE, 'readonly');\n\tconst store = tx.objectStore(CHAT_STORE);\n\tconst source =\n\t\tdeckId === undefined\n\t\t\t? store.getAll()\n\t\t\t: store.index(DECK_INDEX).getAll(IDBKeyRange.only(deckId));\n\tconst rows = await requestToPromise<PptxAiStoredChat[]>(source);\n\tawait transactionDone(tx);\n\treturn rows;\n}\n\n/** Create an IndexedDB-backed {@link ChatBackend}. */\nexport function createIdbBackend(db: IDBDatabase): ChatBackend {\n\treturn {\n\t\tasync list(deckId: string | undefined): Promise<PptxAiChatSummary[]> {\n\t\t\tconst rows = await readAll(db, deckId);\n\t\t\treturn rows.map(toChatSummary).sort((a, b) => b.updatedAt - a.updatedAt);\n\t\t},\n\t\tasync load(id: string): Promise<PptxAiStoredChat | null> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readonly');\n\t\t\tconst row = await requestToPromise<PptxAiStoredChat | undefined>(\n\t\t\t\ttx.objectStore(CHAT_STORE).get(id),\n\t\t\t);\n\t\t\tawait transactionDone(tx);\n\t\t\treturn row ?? null;\n\t\t},\n\t\tasync save(chat: PptxAiStoredChat): Promise<void> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readwrite');\n\t\t\ttx.objectStore(CHAT_STORE).put(chat);\n\t\t\tawait transactionDone(tx);\n\t\t},\n\t\tasync remove(id: string): Promise<void> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readwrite');\n\t\t\ttx.objectStore(CHAT_STORE).delete(id);\n\t\t\tawait transactionDone(tx);\n\t\t},\n\t\tasync clear(deckId: string | undefined): Promise<void> {\n\t\t\tconst tx = db.transaction(CHAT_STORE, 'readwrite');\n\t\t\tconst store = tx.objectStore(CHAT_STORE);\n\t\t\tif (deckId === undefined) {\n\t\t\t\tstore.clear();\n\t\t\t} else {\n\t\t\t\tconst ids = await requestToPromise<IDBValidKey[]>(\n\t\t\t\t\tstore.index(DECK_INDEX).getAllKeys(IDBKeyRange.only(deckId)),\n\t\t\t\t);\n\t\t\t\tfor (const id of ids) {\n\t\t\t\t\tstore.delete(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait transactionDone(tx);\n\t\t},\n\t};\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { CanvasTexture, SRGBColorSpace, LinearFilter, RepeatWrapping, Shape, ExtrudeGeometry, MeshStandardMaterial, Color, Mesh, EdgesGeometry, LineBasicMaterial, LineSegments, PlaneGeometry, MeshBasicMaterial, Euler, Vector3, BufferGeometry, Line, Group, WebGLRenderer, Scene, PerspectiveCamera, AmbientLight, DirectionalLight } from 'three';
1
+ import { CanvasTexture, SRGBColorSpace, LinearFilter, RepeatWrapping, Shape, ExtrudeGeometry, MeshStandardMaterial, Color, Mesh, EdgesGeometry, LineBasicMaterial, LineSegments, PlaneGeometry, MeshBasicMaterial, DoubleSide, Euler, Vector3, BufferGeometry, Line, Group, WebGLRenderer, Scene, PerspectiveCamera, AmbientLight, DirectionalLight } from 'three';
2
2
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
3
3
 
4
4
  /**
@@ -191,12 +191,33 @@ function buildLabelPlane(m, textStyle) {
191
191
  return null;
192
192
  }
193
193
  const planeGeo = new PlaneGeometry(tex.worldWidth, tex.worldHeight);
194
+ // Double-sided: spatial layouts (the cycle/radial carousel ring) rotate
195
+ // roughly half the nodes so their front face points away from the
196
+ // camera's default position. A single-sided (default `FrontSide`)
197
+ // material culls that view entirely, so those nodes render with no
198
+ // caption at all; double-siding keeps the label visible (mirrored, on
199
+ // the far side) instead of vanishing. `scene.ts`'s render loop billboards
200
+ // every plane toward the camera each frame, which overrides this initial
201
+ // rotation once mounted (see `BuiltMeshGroup.forEachLabelPlane`); this
202
+ // still matters for a caller that never renders a frame (tests) and as
203
+ // the visible starting orientation before the first billboard update.
204
+ // `depthTest: false` also matters once billboarded: the plane's offset
205
+ // (below) clears the block along the block's OWN original facing
206
+ // direction, which is not necessarily toward the camera once the plane
207
+ // itself has been rotated to face the camera instead, so a depth-tested
208
+ // plane could end up partially behind its own (or a neighbouring) node's
209
+ // extrusion from the camera's actual angle. Skipping the depth test
210
+ // keeps every caption drawn on top, which is the legible outcome we
211
+ // want for a small, sparse set of node labels.
194
212
  const planeMaterial = new MeshBasicMaterial({
195
213
  map: tex.texture,
196
214
  transparent: true,
197
215
  depthWrite: false,
216
+ depthTest: false,
217
+ side: DoubleSide,
198
218
  });
199
219
  const plane = new Mesh(planeGeo, planeMaterial);
220
+ plane.renderOrder = 1;
200
221
  // Float just past the front (+z) face, clearing any bevel, following the
201
222
  // mesh's rotation so the label sits flat on the (possibly rotated) face.
202
223
  const euler = new Euler(m.rotation.x, m.rotation.y, m.rotation.z);
@@ -238,6 +259,18 @@ function clearLabel(group, entry) {
238
259
  * `textStyle` is a font-style emphasis override applied to every node's
239
260
  * label (see {@link BuiltMeshGroup.setTextStyle}); emphasis is authored per
240
261
  * shape/animation-target, not per SmartArt node, so it applies uniformly.
262
+ *
263
+ * A label plane's *position* floats just off its node's own front (radially
264
+ * outward, for a spatial/carousel arrangement) face, following the node's
265
+ * rotation - but its *rotation* is left at that same face-following value
266
+ * only as an initial default. `mountSmartArt3D`'s render loop re-orients
267
+ * every plane toward the camera each frame via {@link BuiltMeshGroup.forEachLabelPlane}
268
+ * (a Y-axis-only billboard, so captions stay upright and legible). Without
269
+ * that, a node whose front face happens to point away from or across the
270
+ * camera - unavoidable for roughly half of any ring/carousel arrangement -
271
+ * would have an edge-on or backward-facing caption: `DoubleSide` (see
272
+ * `buildLabelPlane`) fixes the "backward" case, but nothing short of facing
273
+ * the plane at the camera fixes "edge-on".
241
274
  */
242
275
  function buildMeshGroup(model, textStyle) {
243
276
  const group = new Group();
@@ -264,6 +297,13 @@ function buildMeshGroup(model, textStyle) {
264
297
  }
265
298
  }
266
299
  },
300
+ forEachLabelPlane(fn) {
301
+ for (const entry of labelEntries) {
302
+ if (entry.plane) {
303
+ fn(entry.plane);
304
+ }
305
+ }
306
+ },
267
307
  dispose() {
268
308
  for (const d of blockDisposables) {
269
309
  d.dispose();
@@ -276,17 +316,43 @@ function buildMeshGroup(model, textStyle) {
276
316
  }
277
317
 
278
318
  /**
279
- * Three.js SmartArt renderer - vanilla scene runtime.
319
+ * Three.js SmartArt renderer - camera framing + label billboarding math.
280
320
  *
281
- * Frames a {@link SmartArt3DModel} in a WebGL scene (lights, perspective
282
- * camera, optional OrbitControls, render loop) on a caller-provided canvas.
283
- * Pure vanilla three.js - no framework code - so the React, Vue, and Angular
284
- * bindings all mount it through a thin canvas wrapper. `three` is imported here
285
- * only; this module lives behind the `pptx-viewer-shared/smartart-3d` subpath so
286
- * it is lazily loaded and `three` stays an optional dependency.
321
+ * Pure geometry (no `three` rendering calls beyond the `Mesh`/`PerspectiveCamera`
322
+ * types billboarding reads from) split out of `scene.ts` to keep that module
323
+ * focused on mount/render-loop orchestration. Exported for unit tests
324
+ * (`scene-camera.test.ts`); not part of the `pptx-viewer-shared/smartart-3d`
325
+ * public subpath barrel (`index.ts`), which only re-exports `mountSmartArt3D`.
287
326
  */
327
+ /** Vertical field of view (degrees) the mounted camera uses. */
288
328
  const FOV = 42;
289
- /** Bounding sphere of all meshes (expanded by footprint/depth) + connectors. */
329
+ /**
330
+ * Expand `bounds` by one mesh's true world-space footprint.
331
+ *
332
+ * Every mesh here only ever rotates about Y (see `applySpatialLayout`), so a
333
+ * mesh's local box - footprint half-extents `(halfWidth, halfHeight)` in its
334
+ * own XY plane, and an extrusion that spans local z from `0` to
335
+ * `depth` (plus a small bevel overshoot on both ends) - can be exactly
336
+ * reprojected into world X/Z with the standard rotated-box formula
337
+ * (`|halfExtent * cos| + |otherHalfExtent * sin|` per axis). Using a single
338
+ * scalar radius applied equally to every world axis (the previous approach)
339
+ * conflated the footprint size with the much smaller extrusion depth,
340
+ * wildly overstating a wide/flat node's extent along its through-face axis
341
+ * and pushing the fit-to-frame camera far past where the content actually
342
+ * needed it, so the whole diagram rendered tiny inside its frame.
343
+ */
344
+ function expandForMesh(expand, m) {
345
+ const hx = m.halfWidth + m.bevel;
346
+ const hy = m.halfHeight + m.bevel;
347
+ const hz = m.depth / 2 + m.bevel;
348
+ const midZ = m.position.z + m.depth / 2;
349
+ const cosY = Math.cos(m.rotation.y);
350
+ const sinY = Math.sin(m.rotation.y);
351
+ const worldHalfX = Math.abs(hx * cosY) + Math.abs(hz * sinY);
352
+ const worldHalfZ = Math.abs(hx * sinY) + Math.abs(hz * cosY);
353
+ expand(m.position.x, m.position.y, midZ, worldHalfX, hy, worldHalfZ);
354
+ }
355
+ /** Bounding sphere of all meshes (rotation-aware) + connectors. */
290
356
  function contentSphere(model) {
291
357
  let minX = Infinity;
292
358
  let minY = Infinity;
@@ -294,32 +360,36 @@ function contentSphere(model) {
294
360
  let maxX = -Infinity;
295
361
  let maxY = -Infinity;
296
362
  let maxZ = -Infinity;
297
- const expand = (x, y, z, r) => {
298
- minX = Math.min(minX, x - r);
299
- minY = Math.min(minY, y - r);
300
- minZ = Math.min(minZ, z - r);
301
- maxX = Math.max(maxX, x + r);
302
- maxY = Math.max(maxY, y + r);
303
- maxZ = Math.max(maxZ, z + r);
363
+ const expand = (x, y, z, rx, ry, rz) => {
364
+ minX = Math.min(minX, x - rx);
365
+ minY = Math.min(minY, y - ry);
366
+ minZ = Math.min(minZ, z - rz);
367
+ maxX = Math.max(maxX, x + rx);
368
+ maxY = Math.max(maxY, y + ry);
369
+ maxZ = Math.max(maxZ, z + rz);
304
370
  };
305
371
  for (const m of model.meshes) {
306
- const r = Math.max(m.halfWidth, m.halfHeight) + m.depth + m.bevel;
307
- expand(m.position.x, m.position.y, m.position.z, r);
372
+ expandForMesh(expand, m);
308
373
  }
309
374
  for (const c of model.connectors) {
310
375
  for (const p of c.points) {
311
- expand(p.x, p.y, p.z, 1);
376
+ expand(p.x, p.y, p.z, 1, 1, 1);
312
377
  }
313
378
  }
314
379
  if (!Number.isFinite(minX)) {
315
- const fallback = Math.max(model.bounds.width, model.bounds.height) / 2 || 1;
316
- return { cx: 0, cy: 0, cz: 0, radius: fallback };
380
+ const halfW = model.bounds.width / 2 || 1;
381
+ const halfH = model.bounds.height / 2 || 1;
382
+ const fallback = Math.max(halfW, halfH);
383
+ return { cx: 0, cy: 0, cz: 0, radius: fallback, halfW, halfH, halfD: 1 };
317
384
  }
318
385
  return {
319
386
  cx: (minX + maxX) / 2,
320
387
  cy: (minY + maxY) / 2,
321
388
  cz: (minZ + maxZ) / 2,
322
389
  radius: 0.5 * Math.hypot(maxX - minX, maxY - minY, maxZ - minZ) || 1,
390
+ halfW: (maxX - minX) / 2,
391
+ halfH: (maxY - minY) / 2,
392
+ halfD: (maxZ - minZ) / 2,
323
393
  };
324
394
  }
325
395
  /** Camera distance that frames a bounding sphere of `radius` at the given FOV. */
@@ -329,6 +399,161 @@ function frameDistance(radius, aspect) {
329
399
  const minFov = Math.min(vFov, hFov);
330
400
  return (radius / Math.sin(minFov / 2)) * 1.1;
331
401
  }
402
+ /** Fraction of the frame the content's projected box may occupy on its tightest axis. */
403
+ const FRAME_FILL = 0.92;
404
+ /**
405
+ * Place the camera in front of the content: a slight x offset + elevation
406
+ * gives the extrusion/spatial depth a readable three-quarter presence while
407
+ * framing the content's own centroid (`cameraElevation` picks the tilt).
408
+ */
409
+ function placeCamera(bounds, family, dist) {
410
+ const { cx, cy, cz, radius } = bounds;
411
+ const elevation = cameraElevation(family, radius, dist);
412
+ return {
413
+ position: [cx + radius * 0.25, cy + elevation, cz + dist],
414
+ target: [cx, cy, cz],
415
+ dist,
416
+ };
417
+ }
418
+ /**
419
+ * Largest ratio of a content-box corner's projected offset to the frame's
420
+ * half-extent, seen from `placement`: `1` means the box exactly touches the
421
+ * frame edge on its tightest axis, `>1` means it overflows. Corners behind
422
+ * the camera count as infinitely overflowing.
423
+ */
424
+ function projectedFill(bounds, placement, aspect) {
425
+ const vFov = (FOV * Math.PI) / 180;
426
+ const tanV = Math.tan(vFov / 2);
427
+ const tanH = tanV * aspect;
428
+ const [px, py, pz] = placement.position;
429
+ const [tx, ty, tz] = placement.target;
430
+ // Camera basis: forward toward the target, right = forward x worldUp, up = right x forward.
431
+ let fx = tx - px, fy = ty - py, fz = tz - pz;
432
+ const fl = Math.hypot(fx, fy, fz) || 1;
433
+ fx /= fl;
434
+ fy /= fl;
435
+ fz /= fl;
436
+ let rx = -fz, rz = fx;
437
+ const rl = Math.hypot(rx, rz) || 1;
438
+ rx /= rl;
439
+ rz /= rl;
440
+ const ux = -rz * fy, uy = rz * fx - rx * fz, uz = rx * fy;
441
+ let worst = 0;
442
+ for (const sx of [-1, 1]) {
443
+ for (const sy of [-1, 1]) {
444
+ for (const sz of [-1, 1]) {
445
+ const dx = bounds.cx + sx * bounds.halfW - px;
446
+ const dy = bounds.cy + sy * bounds.halfH - py;
447
+ const dz = bounds.cz + sz * bounds.halfD - pz;
448
+ const depth = dx * fx + dy * fy + dz * fz;
449
+ if (depth <= 1e-6) {
450
+ return Infinity;
451
+ }
452
+ const x = Math.abs(dx * rx + dz * rz) / (depth * tanH);
453
+ const y = Math.abs(dx * ux + dy * uy + dz * uz) / (depth * tanV);
454
+ worst = Math.max(worst, x, y);
455
+ }
456
+ }
457
+ }
458
+ return worst;
459
+ }
460
+ /**
461
+ * Camera placement whose view fits the content's projected bounding BOX to
462
+ * the frame (`FRAME_FILL` of the tightest axis).
463
+ *
464
+ * `frameDistance` fits the bounding SPHERE to the smaller field of view,
465
+ * which is only tight for roughly cubic content: a wide, flat list diagram
466
+ * (600 x 340 x 40) has a sphere nearly as wide as the diagram but the frame
467
+ * is 1.76x wider than it is tall, so the sphere fit left the diagram at ~45%
468
+ * of the frame width with its captions unreadably small. Starting from the
469
+ * sphere fit, this re-projects the box's eight corners through the actual
470
+ * (elevated, x-offset) camera and rescales the distance until the box just
471
+ * fills the frame; the ratio is close to linear in `dist`, so a handful of
472
+ * fixed-point steps converge.
473
+ */
474
+ function fitCamera(bounds, family, aspect) {
475
+ let dist = frameDistance(bounds.radius, aspect);
476
+ let placement = placeCamera(bounds, family, dist);
477
+ for (let step = 0; step < 8; step++) {
478
+ const fill = projectedFill(bounds, placement, aspect);
479
+ if (!Number.isFinite(fill)) {
480
+ dist *= 2;
481
+ }
482
+ else {
483
+ const ratio = fill / FRAME_FILL;
484
+ if (Math.abs(ratio - 1) < 0.01) {
485
+ break;
486
+ }
487
+ dist *= ratio;
488
+ }
489
+ placement = placeCamera(bounds, family, dist);
490
+ }
491
+ return placement;
492
+ }
493
+ /** Elevation angle (degrees, above the content's own horizontal plane) for a near-overhead ring view. */
494
+ const CAROUSEL_ELEVATION_DEG = 34;
495
+ /**
496
+ * World-space Y offset for the camera above the content's own centroid.
497
+ *
498
+ * `cycleSpatial` (see `smartart-3d-spatial.ts`) flattens every node onto
499
+ * `y = 0`, arranging them on a ring in the XZ plane. The default modest
500
+ * elevation (a good three-quarter tilt for extruded-flat and receding-tree
501
+ * content, which both keep real vertical extent) views that flat ring
502
+ * almost edge-on: nodes on the far side of the ring sit almost directly
503
+ * behind the near ones from the camera's angle, so the diagram reads as a
504
+ * couple of overlapping shapes instead of the circle the 2D layout shows.
505
+ * A steep, near-overhead elevation (expressed as a fixed angle, not a
506
+ * fraction of `radius`, so it holds regardless of the radius/distance
507
+ * ratio) opens the ring up into a readable circle, matching the 2D
508
+ * cycle/radial layout's own top-down gestalt.
509
+ */
510
+ function cameraElevation(family, radius, dist) {
511
+ if (family === 'cycle' || family === 'radial') {
512
+ return dist * Math.tan((CAROUSEL_ELEVATION_DEG * Math.PI) / 180);
513
+ }
514
+ return radius * 0.3;
515
+ }
516
+ /**
517
+ * Y-axis-only billboard: rotate every label plane to face the camera's
518
+ * current azimuth, keeping the plane vertical (so captions never tilt or
519
+ * flip upside down).
520
+ *
521
+ * A label's position floats just off its node's own (possibly rotated,
522
+ * possibly radially-outward-facing) front face, but a plane rotated to
523
+ * match that face is only readable when the camera happens to be roughly
524
+ * in front of it. For a spatial/carousel arrangement (`cycleSpatial`),
525
+ * that is true for at most the ~180 degrees of the ring facing the
526
+ * camera, and only good for reading text within roughly +/-45 degrees of
527
+ * dead-on; nodes further round the ring go edge-on (near-zero projected
528
+ * width; `DoubleSide` on the material does not help, since that only
529
+ * fixes a plane facing directly away, not sideways) or fully backward.
530
+ * Re-orienting the plane itself toward the camera, every frame (so this
531
+ * keeps working through an interactive orbit), makes every node's caption
532
+ * legible regardless of which way its underlying block happens to face.
533
+ */
534
+ function billboardLabels(built, camera) {
535
+ built.forEachLabelPlane((plane) => {
536
+ const dx = camera.position.x - plane.position.x;
537
+ const dz = camera.position.z - plane.position.z;
538
+ plane.rotation.set(0, Math.atan2(dx, dz), 0);
539
+ });
540
+ }
541
+
542
+ /**
543
+ * Three.js SmartArt renderer - vanilla scene runtime.
544
+ *
545
+ * Frames a {@link SmartArt3DModel} in a WebGL scene (lights, perspective
546
+ * camera, optional OrbitControls, render loop) on a caller-provided canvas.
547
+ * Pure vanilla three.js - no framework code - so the React, Vue, and Angular
548
+ * bindings all mount it through a thin canvas wrapper. `three` is imported here
549
+ * only; this module lives behind the `pptx-viewer-shared/smartart-3d` subpath so
550
+ * it is lazily loaded and `three` stays an optional dependency.
551
+ *
552
+ * Camera framing (`contentSphere`/`fitCamera`/`cameraElevation`) and label
553
+ * billboarding (`billboardLabels`) are pure geometry, split out to
554
+ * `scene-camera.ts` to keep this module focused on mount/render-loop
555
+ * orchestration.
556
+ */
332
557
  /**
333
558
  * Mount a SmartArt 3D model onto a canvas and start rendering.
334
559
  *
@@ -342,14 +567,17 @@ function mountSmartArt3D(canvas, model, width, height, options = {}) {
342
567
  if (options.background) {
343
568
  scene.background = new Color(options.background);
344
569
  }
345
- const { cx, cy, cz, radius } = contentSphere(model);
570
+ const bounds = contentSphere(model);
571
+ const { cx, cy, cz, radius } = bounds;
346
572
  const aspect = width / Math.max(1, height);
347
- const dist = frameDistance(radius, aspect);
573
+ // Fit the content's projected bounding box (not just its sphere) to the
574
+ // frame; see `fitCamera` for why the sphere fit alone left wide, flat
575
+ // diagrams tiny.
576
+ const placement = fitCamera(bounds, model.family, aspect);
577
+ const { dist } = placement;
348
578
  const camera = new PerspectiveCamera(FOV, aspect, 0.1, dist * 8 + radius * 4);
349
- // A slight elevation + offset gives the extrusion/spatial depth a readable
350
- // three-quarter presence, framing the content's own centroid.
351
- camera.position.set(cx + radius * 0.25, cy + radius * 0.3, cz + dist);
352
- camera.lookAt(cx, cy, cz);
579
+ camera.position.set(...placement.position);
580
+ camera.lookAt(...placement.target);
353
581
  scene.add(new AmbientLight(0xffffff, 0.62));
354
582
  const key = new DirectionalLight(0xffffff, 0.95);
355
583
  key.position.set(cx + radius, cy + radius * 1.4, cz + dist);
@@ -386,6 +614,7 @@ function mountSmartArt3D(canvas, model, width, height, options = {}) {
386
614
  return;
387
615
  }
388
616
  frame = requestAnimationFrame(renderLoop);
617
+ billboardLabels(built, camera);
389
618
  controls?.update();
390
619
  renderer.render(scene, camera);
391
620
  };
@@ -423,4 +652,4 @@ function mountSmartArt3D(canvas, model, width, height, options = {}) {
423
652
  */
424
653
 
425
654
  export { mountSmartArt3D };
426
- //# sourceMappingURL=pptx-angular-viewer-index-PoHn1XJd.mjs.map
655
+ //# sourceMappingURL=pptx-angular-viewer-index-7b9uo9P2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pptx-angular-viewer-index-7b9uo9P2.mjs","sources":["../../src/internal/shared-src/smartart-3d/text-texture.ts","../../src/internal/shared-src/smartart-3d/meshes.ts","../../src/internal/shared-src/smartart-3d/scene-camera.ts","../../src/internal/shared-src/smartart-3d/scene.ts","../../src/internal/shared-src/smartart-3d/index.ts"],"sourcesContent":["/**\n * Three.js SmartArt renderer - front-face text textures.\n *\n * Renders a node label onto an offscreen 2D canvas and wraps it as a\n * `THREE.CanvasTexture`, sized to sit on the extruded block's front face. Text\n * is word-wrapped and auto-shrunk to fit the node footprint. Returns `null` in\n * non-DOM environments (SSR / unit tests).\n */\n\nimport { CanvasTexture, LinearFilter, RepeatWrapping, SRGBColorSpace } from 'three';\n\nimport type { TextStyleAnimationDescriptor } from '../render/animation-text-style-resolve';\n\n/** A built label texture plus the world-space plane size it should fill. */\nexport interface SmartArtTextTexture {\n\ttexture: CanvasTexture;\n\t/** Plane width in world (layout-pixel) units. */\n\tworldWidth: number;\n\t/** Plane height in world units. */\n\tworldHeight: number;\n}\n\n/** Supersampling factor for crisp text at oblique camera angles. */\nconst SUPERSAMPLE = 4;\n/** Fraction of the footprint the text plane occupies (inset padding). */\nconst FILL = 0.86;\n\n/** Greedily word-wrap `text` to lines that fit `maxWidth` at the given font. */\nfunction wrapLines(ctx: CanvasRenderingContext2D, text: string, maxWidth: number): string[] {\n\tconst words = text.split(/\\s+/u).filter(Boolean);\n\tif (words.length === 0) {\n\t\treturn [];\n\t}\n\tconst lines: string[] = [];\n\tlet line = words[0];\n\tfor (let i = 1; i < words.length; i++) {\n\t\tconst candidate = `${line} ${words[i]}`;\n\t\tif (ctx.measureText(candidate).width <= maxWidth) {\n\t\t\tline = candidate;\n\t\t} else {\n\t\t\tlines.push(line);\n\t\t\tline = words[i];\n\t\t}\n\t}\n\tlines.push(line);\n\treturn lines;\n}\n\n/** Resolve the canvas `font` string weight/style + effective colour for an optional emphasis override. */\nfunction resolveEmphasisFont(\n\tcolor: string,\n\temphasis: TextStyleAnimationDescriptor | undefined,\n): { weight: string; italic: string; color: string; scale: number } {\n\tconst weight = emphasis?.bold === undefined ? '600' : emphasis.bold ? '700' : '400';\n\tconst italic = emphasis?.italic ? 'italic ' : '';\n\tconst scale =\n\t\ttypeof emphasis?.fontScale === 'number' &&\n\t\tNumber.isFinite(emphasis.fontScale) &&\n\t\temphasis.fontScale > 0\n\t\t\t? emphasis.fontScale\n\t\t\t: 1;\n\treturn { weight, italic, color: emphasis?.color ?? color, scale };\n}\n\n/**\n * Build a label texture for a node's front face.\n *\n * @param text Label text.\n * @param color CSS colour for the text.\n * @param fontSize Requested font size in layout pixels.\n * @param footW Node footprint width (layout pixels).\n * @param footH Node footprint height (layout pixels).\n * @param emphasis Active font-style emphasis override (bold/italic/underline/size/colour), if any.\n * @returns The texture + plane size, or `null` when no DOM / empty text.\n */\nexport function makeTextTexture(\n\ttext: string,\n\tcolor: string,\n\tfontSize: number,\n\tfootW: number,\n\tfootH: number,\n\temphasis?: TextStyleAnimationDescriptor,\n): SmartArtTextTexture | null {\n\tif (typeof document === 'undefined' || !text.trim() || footW <= 0 || footH <= 0) {\n\t\treturn null;\n\t}\n\n\tconst { weight, italic, color: effectiveColor, scale } = resolveEmphasisFont(color, emphasis);\n\tconst worldWidth = footW * FILL;\n\tconst worldHeight = footH * FILL;\n\tconst canvas = document.createElement('canvas');\n\tcanvas.width = Math.max(8, Math.round(worldWidth * SUPERSAMPLE));\n\tcanvas.height = Math.max(8, Math.round(worldHeight * SUPERSAMPLE));\n\tconst ctx = canvas.getContext('2d');\n\tif (!ctx) {\n\t\treturn null;\n\t}\n\n\tconst family = 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif';\n\tconst maxTextWidth = canvas.width * 0.94;\n\tconst fontString = (px: number): string => `${italic}${weight} ${px}px ${family}`;\n\n\t// Shrink the font until the wrapped block fits the canvas height.\n\tlet px = Math.max(6, fontSize) * scale * SUPERSAMPLE;\n\tlet lines: string[] = [];\n\tfor (let attempt = 0; attempt < 12; attempt++) {\n\t\tctx.font = fontString(px);\n\t\tlines = wrapLines(ctx, text, maxTextWidth);\n\t\tconst lineHeight = px * 1.2;\n\t\tif (lines.length * lineHeight <= canvas.height * 0.96 || px <= 6 * SUPERSAMPLE) {\n\t\t\tbreak;\n\t\t}\n\t\tpx *= 0.88;\n\t}\n\n\tctx.clearRect(0, 0, canvas.width, canvas.height);\n\tctx.fillStyle = effectiveColor;\n\tctx.textAlign = 'center';\n\tctx.textBaseline = 'middle';\n\tctx.font = fontString(px);\n\tconst lineHeight = px * 1.2;\n\tconst blockHeight = lines.length * lineHeight;\n\tconst startY = canvas.height / 2 - blockHeight / 2 + lineHeight / 2;\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst y = startY + i * lineHeight;\n\t\tctx.fillText(lines[i], canvas.width / 2, y);\n\t\tif (emphasis?.underline) {\n\t\t\tconst w = ctx.measureText(lines[i]).width;\n\t\t\tctx.strokeStyle = effectiveColor;\n\t\t\tctx.lineWidth = Math.max(1, px * 0.06);\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(canvas.width / 2 - w / 2, y + px * 0.32);\n\t\t\tctx.lineTo(canvas.width / 2 + w / 2, y + px * 0.32);\n\t\t\tctx.stroke();\n\t\t}\n\t}\n\n\tconst texture = new CanvasTexture(canvas);\n\ttexture.colorSpace = SRGBColorSpace;\n\ttexture.minFilter = LinearFilter;\n\ttexture.magFilter = LinearFilter;\n\t// Disable GPU-side flip: WebGL2 does not allow UNPACK_FLIP_Y_WEBGL for\n\t// texImage3D targets (depth buffers, LUTs, shadow maps). Leaving flipY=true\n\t// (the Three.js default) pollutes the global pixel-store state and causes\n\t// \"INVALID_OPERATION: texImage3D: FLIP_Y or PREMULTIPLY_ALPHA isn't allowed\"\n\t// errors. Compensate in UV space instead.\n\ttexture.flipY = false;\n\ttexture.premultiplyAlpha = false;\n\ttexture.wrapT = RepeatWrapping;\n\ttexture.repeat.set(1, -1);\n\ttexture.offset.set(0, 1);\n\ttexture.needsUpdate = true;\n\n\treturn { texture, worldWidth, worldHeight };\n}\n","/**\n * Three.js SmartArt renderer - mesh-group construction.\n *\n * Turns a pure {@link SmartArt3DModel} into a `THREE.Group` of extruded blocks\n * (with bevels, edge outlines, and front-face text planes) plus connector\n * lines. All allocated GPU resources are tracked so the caller can dispose them\n * deterministically.\n */\n\nimport {\n\tBufferGeometry,\n\tColor,\n\tDoubleSide,\n\tEdgesGeometry,\n\tEuler,\n\tExtrudeGeometry,\n\tGroup,\n\tLine,\n\tLineBasicMaterial,\n\tLineSegments,\n\tMesh,\n\tMeshBasicMaterial,\n\tMeshStandardMaterial,\n\tPlaneGeometry,\n\tShape,\n\tVector3,\n} from 'three';\n\nimport type { TextStyleAnimationDescriptor } from '../render/animation-text-style-resolve';\nimport type { SmartArt3DMesh, SmartArt3DModel } from '../render/smartart-3d-types';\nimport { makeTextTexture } from './text-texture';\n\n/** A disposable GPU resource (geometry, material, or texture). */\ninterface Disposable {\n\tdispose: () => void;\n}\n\n/** A built mesh group plus its teardown hook. */\nexport interface BuiltMeshGroup {\n\tgroup: Group;\n\t/**\n\t * Rebuild every node's label plane with a new font-style emphasis override\n\t * (or `undefined` to clear it) - the LABEL textures are baked into a canvas\n\t * (see `text-texture.ts`), so applying an emphasis change means disposing\n\t * and repainting them, unlike a CSS override on DOM text. Leaves the block\n\t * extrusions and connectors untouched.\n\t */\n\tsetTextStyle: (style: TextStyleAnimationDescriptor | undefined) => void;\n\t/**\n\t * Call `fn` once per currently-mounted label plane. `scene.ts`'s render\n\t * loop uses this to billboard every label toward the camera each frame\n\t * (see {@link buildMeshGroup}'s docs); iterating live (rather than a\n\t * snapshot array handed out once) means a plane rebuilt by\n\t * `setTextStyle` is picked up automatically.\n\t */\n\tforEachLabelPlane: (fn: (plane: Mesh) => void) => void;\n\tdispose: () => void;\n}\n\n/** Build the extruded geometry for one node. */\nfunction extrudeGeometry(m: SmartArt3DMesh): ExtrudeGeometry {\n\tconst shape = new Shape();\n\tm.outline.forEach((p, i) => {\n\t\tif (i === 0) {\n\t\t\tshape.moveTo(p.x, p.y);\n\t\t} else {\n\t\t\tshape.lineTo(p.x, p.y);\n\t\t}\n\t});\n\tshape.closePath();\n\n\tconst bevelEnabled = m.bevel > 0;\n\treturn new ExtrudeGeometry(shape, {\n\t\tdepth: m.depth,\n\t\tbevelEnabled,\n\t\tbevelThickness: m.bevel,\n\t\tbevelSize: m.bevel,\n\t\tbevelSegments: 2,\n\t\tcurveSegments: m.rounded ? 24 : 1,\n\t\tsteps: 1,\n\t});\n}\n\n/** Add the extruded block + edge outline for one node to the group. */\nfunction addBlock(group: Group, disposables: Disposable[], m: SmartArt3DMesh): ExtrudeGeometry {\n\tconst geo = extrudeGeometry(m);\n\tconst material = new MeshStandardMaterial({\n\t\tcolor: new Color(m.fill),\n\t\tmetalness: 0.12,\n\t\troughness: 0.52,\n\t\ttransparent: m.opacity < 1,\n\t\topacity: m.opacity,\n\t});\n\tconst mesh = new Mesh(geo, material);\n\tmesh.position.set(m.position.x, m.position.y, m.position.z);\n\tmesh.rotation.set(m.rotation.x, m.rotation.y, m.rotation.z);\n\tgroup.add(mesh);\n\tdisposables.push(geo, material);\n\n\tif (m.strokeWidth > 0) {\n\t\tconst edges = new EdgesGeometry(geo, 30);\n\t\tconst lineMaterial = new LineBasicMaterial({ color: new Color(m.stroke) });\n\t\tconst line = new LineSegments(edges, lineMaterial);\n\t\tline.position.copy(mesh.position);\n\t\tline.rotation.copy(mesh.rotation);\n\t\tgroup.add(line);\n\t\tdisposables.push(edges, lineMaterial);\n\t}\n\treturn geo;\n}\n\n/** One node's currently-mounted label plane (or none), tracked for `setTextStyle` rebuilds. */\ninterface LabelEntry {\n\tnode: SmartArt3DMesh;\n\tplane: Mesh | null;\n\tdisposables: Disposable[];\n}\n\n/** Build a front-face text plane for one node, or `null` when it has no label / no DOM. */\nfunction buildLabelPlane(\n\tm: SmartArt3DMesh,\n\ttextStyle: TextStyleAnimationDescriptor | undefined,\n): { plane: Mesh; disposables: Disposable[] } | null {\n\tif (!m.text) {\n\t\treturn null;\n\t}\n\tconst tex = makeTextTexture(\n\t\tm.text,\n\t\tm.textColor,\n\t\tm.fontSize,\n\t\tm.halfWidth * 2,\n\t\tm.halfHeight * 2,\n\t\ttextStyle,\n\t);\n\tif (!tex) {\n\t\treturn null;\n\t}\n\tconst planeGeo = new PlaneGeometry(tex.worldWidth, tex.worldHeight);\n\t// Double-sided: spatial layouts (the cycle/radial carousel ring) rotate\n\t// roughly half the nodes so their front face points away from the\n\t// camera's default position. A single-sided (default `FrontSide`)\n\t// material culls that view entirely, so those nodes render with no\n\t// caption at all; double-siding keeps the label visible (mirrored, on\n\t// the far side) instead of vanishing. `scene.ts`'s render loop billboards\n\t// every plane toward the camera each frame, which overrides this initial\n\t// rotation once mounted (see `BuiltMeshGroup.forEachLabelPlane`); this\n\t// still matters for a caller that never renders a frame (tests) and as\n\t// the visible starting orientation before the first billboard update.\n\t// `depthTest: false` also matters once billboarded: the plane's offset\n\t// (below) clears the block along the block's OWN original facing\n\t// direction, which is not necessarily toward the camera once the plane\n\t// itself has been rotated to face the camera instead, so a depth-tested\n\t// plane could end up partially behind its own (or a neighbouring) node's\n\t// extrusion from the camera's actual angle. Skipping the depth test\n\t// keeps every caption drawn on top, which is the legible outcome we\n\t// want for a small, sparse set of node labels.\n\tconst planeMaterial = new MeshBasicMaterial({\n\t\tmap: tex.texture,\n\t\ttransparent: true,\n\t\tdepthWrite: false,\n\t\tdepthTest: false,\n\t\tside: DoubleSide,\n\t});\n\tconst plane = new Mesh(planeGeo, planeMaterial);\n\tplane.renderOrder = 1;\n\t// Float just past the front (+z) face, clearing any bevel, following the\n\t// mesh's rotation so the label sits flat on the (possibly rotated) face.\n\tconst euler = new Euler(m.rotation.x, m.rotation.y, m.rotation.z);\n\tconst offset = new Vector3(0, 0, m.depth + m.bevel + 0.4).applyEuler(euler);\n\tplane.position.set(m.position.x + offset.x, m.position.y + offset.y, m.position.z + offset.z);\n\tplane.rotation.copy(euler);\n\treturn { plane, disposables: [planeGeo, planeMaterial, tex.texture] };\n}\n\n/** Add a connector poly-line on the base plane. */\nfunction addConnectors(group: Group, disposables: Disposable[], model: SmartArt3DModel): void {\n\tfor (const c of model.connectors) {\n\t\tif (c.points.length < 2) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst geo = new BufferGeometry().setFromPoints(c.points.map((p) => new Vector3(p.x, p.y, p.z)));\n\t\tconst material = new LineBasicMaterial({\n\t\t\tcolor: new Color(c.color),\n\t\t\ttransparent: true,\n\t\t\topacity: 0.7,\n\t\t});\n\t\tgroup.add(new Line(geo, material));\n\t\tdisposables.push(geo, material);\n\t}\n}\n\n/** Remove and dispose one label entry's current plane, if any. */\nfunction clearLabel(group: Group, entry: LabelEntry): void {\n\tif (entry.plane) {\n\t\tgroup.remove(entry.plane);\n\t}\n\tfor (const d of entry.disposables) {\n\t\td.dispose();\n\t}\n\tentry.plane = null;\n\tentry.disposables = [];\n}\n\n/**\n * Build a `THREE.Group` for a SmartArt 3D model. The group is centred on the\n * origin (the model positions already are); callers add it to the scene.\n * `textStyle` is a font-style emphasis override applied to every node's\n * label (see {@link BuiltMeshGroup.setTextStyle}); emphasis is authored per\n * shape/animation-target, not per SmartArt node, so it applies uniformly.\n *\n * A label plane's *position* floats just off its node's own front (radially\n * outward, for a spatial/carousel arrangement) face, following the node's\n * rotation - but its *rotation* is left at that same face-following value\n * only as an initial default. `mountSmartArt3D`'s render loop re-orients\n * every plane toward the camera each frame via {@link BuiltMeshGroup.forEachLabelPlane}\n * (a Y-axis-only billboard, so captions stay upright and legible). Without\n * that, a node whose front face happens to point away from or across the\n * camera - unavoidable for roughly half of any ring/carousel arrangement -\n * would have an edge-on or backward-facing caption: `DoubleSide` (see\n * `buildLabelPlane`) fixes the \"backward\" case, but nothing short of facing\n * the plane at the camera fixes \"edge-on\".\n */\nexport function buildMeshGroup(\n\tmodel: SmartArt3DModel,\n\ttextStyle?: TextStyleAnimationDescriptor,\n): BuiltMeshGroup {\n\tconst group = new Group();\n\tconst blockDisposables: Disposable[] = [];\n\tconst labelEntries: LabelEntry[] = model.meshes.map((m) => {\n\t\taddBlock(group, blockDisposables, m);\n\t\tconst built = buildLabelPlane(m, textStyle);\n\t\tif (built) {\n\t\t\tgroup.add(built.plane);\n\t\t}\n\t\treturn { node: m, plane: built?.plane ?? null, disposables: built?.disposables ?? [] };\n\t});\n\taddConnectors(group, blockDisposables, model);\n\n\treturn {\n\t\tgroup,\n\t\tsetTextStyle(style) {\n\t\t\tfor (const entry of labelEntries) {\n\t\t\t\tclearLabel(group, entry);\n\t\t\t\tconst built = buildLabelPlane(entry.node, style);\n\t\t\t\tif (built) {\n\t\t\t\t\tgroup.add(built.plane);\n\t\t\t\t\tentry.plane = built.plane;\n\t\t\t\t\tentry.disposables = built.disposables;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tforEachLabelPlane(fn) {\n\t\t\tfor (const entry of labelEntries) {\n\t\t\t\tif (entry.plane) {\n\t\t\t\t\tfn(entry.plane);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdispose() {\n\t\t\tfor (const d of blockDisposables) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tfor (const entry of labelEntries) {\n\t\t\t\tclearLabel(group, entry);\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Three.js SmartArt renderer - camera framing + label billboarding math.\n *\n * Pure geometry (no `three` rendering calls beyond the `Mesh`/`PerspectiveCamera`\n * types billboarding reads from) split out of `scene.ts` to keep that module\n * focused on mount/render-loop orchestration. Exported for unit tests\n * (`scene-camera.test.ts`); not part of the `pptx-viewer-shared/smartart-3d`\n * public subpath barrel (`index.ts`), which only re-exports `mountSmartArt3D`.\n */\n\nimport type { Mesh, PerspectiveCamera } from 'three';\n\nimport type { SmartArt3DMesh, SmartArt3DModel } from '../render/smartart-3d-types';\nimport type { BuiltMeshGroup } from './meshes';\n\n/** Vertical field of view (degrees) the mounted camera uses. */\nexport const FOV = 42;\n\n/** A bounding sphere of the 3D content (centre + radius) plus its axis-aligned half-extents. */\nexport interface ContentSphere {\n\tcx: number;\n\tcy: number;\n\tcz: number;\n\tradius: number;\n\t/** World-space half-extent of the content's AABB along X. */\n\thalfW: number;\n\t/** World-space half-extent of the content's AABB along Y. */\n\thalfH: number;\n\t/** World-space half-extent of the content's AABB along Z. */\n\thalfD: number;\n}\n\n/**\n * Expand a running world-space AABB (tracked via the six `min`/`max` closure\n * variables below) by an axis-aligned box centred at `(x, y, z)` with\n * per-axis half-extents `(rx, ry, rz)`.\n */\ntype BoundsExpander = (x: number, y: number, z: number, rx: number, ry: number, rz: number) => void;\n\n/**\n * Expand `bounds` by one mesh's true world-space footprint.\n *\n * Every mesh here only ever rotates about Y (see `applySpatialLayout`), so a\n * mesh's local box - footprint half-extents `(halfWidth, halfHeight)` in its\n * own XY plane, and an extrusion that spans local z from `0` to\n * `depth` (plus a small bevel overshoot on both ends) - can be exactly\n * reprojected into world X/Z with the standard rotated-box formula\n * (`|halfExtent * cos| + |otherHalfExtent * sin|` per axis). Using a single\n * scalar radius applied equally to every world axis (the previous approach)\n * conflated the footprint size with the much smaller extrusion depth,\n * wildly overstating a wide/flat node's extent along its through-face axis\n * and pushing the fit-to-frame camera far past where the content actually\n * needed it, so the whole diagram rendered tiny inside its frame.\n */\nfunction expandForMesh(expand: BoundsExpander, m: SmartArt3DMesh): void {\n\tconst hx = m.halfWidth + m.bevel;\n\tconst hy = m.halfHeight + m.bevel;\n\tconst hz = m.depth / 2 + m.bevel;\n\tconst midZ = m.position.z + m.depth / 2;\n\tconst cosY = Math.cos(m.rotation.y);\n\tconst sinY = Math.sin(m.rotation.y);\n\tconst worldHalfX = Math.abs(hx * cosY) + Math.abs(hz * sinY);\n\tconst worldHalfZ = Math.abs(hx * sinY) + Math.abs(hz * cosY);\n\texpand(m.position.x, m.position.y, midZ, worldHalfX, hy, worldHalfZ);\n}\n\n/** Bounding sphere of all meshes (rotation-aware) + connectors. */\nexport function contentSphere(model: SmartArt3DModel): ContentSphere {\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet minZ = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\tlet maxZ = -Infinity;\n\tconst expand: BoundsExpander = (x, y, z, rx, ry, rz) => {\n\t\tminX = Math.min(minX, x - rx);\n\t\tminY = Math.min(minY, y - ry);\n\t\tminZ = Math.min(minZ, z - rz);\n\t\tmaxX = Math.max(maxX, x + rx);\n\t\tmaxY = Math.max(maxY, y + ry);\n\t\tmaxZ = Math.max(maxZ, z + rz);\n\t};\n\tfor (const m of model.meshes) {\n\t\texpandForMesh(expand, m);\n\t}\n\tfor (const c of model.connectors) {\n\t\tfor (const p of c.points) {\n\t\t\texpand(p.x, p.y, p.z, 1, 1, 1);\n\t\t}\n\t}\n\tif (!Number.isFinite(minX)) {\n\t\tconst halfW = model.bounds.width / 2 || 1;\n\t\tconst halfH = model.bounds.height / 2 || 1;\n\t\tconst fallback = Math.max(halfW, halfH);\n\t\treturn { cx: 0, cy: 0, cz: 0, radius: fallback, halfW, halfH, halfD: 1 };\n\t}\n\treturn {\n\t\tcx: (minX + maxX) / 2,\n\t\tcy: (minY + maxY) / 2,\n\t\tcz: (minZ + maxZ) / 2,\n\t\tradius: 0.5 * Math.hypot(maxX - minX, maxY - minY, maxZ - minZ) || 1,\n\t\thalfW: (maxX - minX) / 2,\n\t\thalfH: (maxY - minY) / 2,\n\t\thalfD: (maxZ - minZ) / 2,\n\t};\n}\n\n/** Camera distance that frames a bounding sphere of `radius` at the given FOV. */\nexport function frameDistance(radius: number, aspect: number): number {\n\tconst vFov = (FOV * Math.PI) / 180;\n\tconst hFov = 2 * Math.atan(Math.tan(vFov / 2) * aspect);\n\tconst minFov = Math.min(vFov, hFov);\n\treturn (radius / Math.sin(minFov / 2)) * 1.1;\n}\n\n/** Fraction of the frame the content's projected box may occupy on its tightest axis. */\nconst FRAME_FILL = 0.92;\n\n/** The camera position for a given z-offset `dist`, plus the point it looks at. */\nexport interface CameraPlacement {\n\tposition: [number, number, number];\n\ttarget: [number, number, number];\n\t/** The z-offset the placement was derived from (OrbitControls distance limits scale from it). */\n\tdist: number;\n}\n\n/**\n * Place the camera in front of the content: a slight x offset + elevation\n * gives the extrusion/spatial depth a readable three-quarter presence while\n * framing the content's own centroid (`cameraElevation` picks the tilt).\n */\nexport function placeCamera(\n\tbounds: ContentSphere,\n\tfamily: SmartArt3DModel['family'],\n\tdist: number,\n): CameraPlacement {\n\tconst { cx, cy, cz, radius } = bounds;\n\tconst elevation = cameraElevation(family, radius, dist);\n\treturn {\n\t\tposition: [cx + radius * 0.25, cy + elevation, cz + dist],\n\t\ttarget: [cx, cy, cz],\n\t\tdist,\n\t};\n}\n\n/**\n * Largest ratio of a content-box corner's projected offset to the frame's\n * half-extent, seen from `placement`: `1` means the box exactly touches the\n * frame edge on its tightest axis, `>1` means it overflows. Corners behind\n * the camera count as infinitely overflowing.\n */\nfunction projectedFill(bounds: ContentSphere, placement: CameraPlacement, aspect: number): number {\n\tconst vFov = (FOV * Math.PI) / 180;\n\tconst tanV = Math.tan(vFov / 2);\n\tconst tanH = tanV * aspect;\n\tconst [px, py, pz] = placement.position;\n\tconst [tx, ty, tz] = placement.target;\n\t// Camera basis: forward toward the target, right = forward x worldUp, up = right x forward.\n\tlet fx = tx - px,\n\t\tfy = ty - py,\n\t\tfz = tz - pz;\n\tconst fl = Math.hypot(fx, fy, fz) || 1;\n\tfx /= fl;\n\tfy /= fl;\n\tfz /= fl;\n\tlet rx = -fz,\n\t\trz = fx;\n\tconst rl = Math.hypot(rx, rz) || 1;\n\trx /= rl;\n\trz /= rl;\n\tconst ux = -rz * fy,\n\t\tuy = rz * fx - rx * fz,\n\t\tuz = rx * fy;\n\tlet worst = 0;\n\tfor (const sx of [-1, 1]) {\n\t\tfor (const sy of [-1, 1]) {\n\t\t\tfor (const sz of [-1, 1]) {\n\t\t\t\tconst dx = bounds.cx + sx * bounds.halfW - px;\n\t\t\t\tconst dy = bounds.cy + sy * bounds.halfH - py;\n\t\t\t\tconst dz = bounds.cz + sz * bounds.halfD - pz;\n\t\t\t\tconst depth = dx * fx + dy * fy + dz * fz;\n\t\t\t\tif (depth <= 1e-6) {\n\t\t\t\t\treturn Infinity;\n\t\t\t\t}\n\t\t\t\tconst x = Math.abs(dx * rx + dz * rz) / (depth * tanH);\n\t\t\t\tconst y = Math.abs(dx * ux + dy * uy + dz * uz) / (depth * tanV);\n\t\t\t\tworst = Math.max(worst, x, y);\n\t\t\t}\n\t\t}\n\t}\n\treturn worst;\n}\n\n/**\n * Camera placement whose view fits the content's projected bounding BOX to\n * the frame (`FRAME_FILL` of the tightest axis).\n *\n * `frameDistance` fits the bounding SPHERE to the smaller field of view,\n * which is only tight for roughly cubic content: a wide, flat list diagram\n * (600 x 340 x 40) has a sphere nearly as wide as the diagram but the frame\n * is 1.76x wider than it is tall, so the sphere fit left the diagram at ~45%\n * of the frame width with its captions unreadably small. Starting from the\n * sphere fit, this re-projects the box's eight corners through the actual\n * (elevated, x-offset) camera and rescales the distance until the box just\n * fills the frame; the ratio is close to linear in `dist`, so a handful of\n * fixed-point steps converge.\n */\nexport function fitCamera(\n\tbounds: ContentSphere,\n\tfamily: SmartArt3DModel['family'],\n\taspect: number,\n): CameraPlacement {\n\tlet dist = frameDistance(bounds.radius, aspect);\n\tlet placement = placeCamera(bounds, family, dist);\n\tfor (let step = 0; step < 8; step++) {\n\t\tconst fill = projectedFill(bounds, placement, aspect);\n\t\tif (!Number.isFinite(fill)) {\n\t\t\tdist *= 2;\n\t\t} else {\n\t\t\tconst ratio = fill / FRAME_FILL;\n\t\t\tif (Math.abs(ratio - 1) < 0.01) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdist *= ratio;\n\t\t}\n\t\tplacement = placeCamera(bounds, family, dist);\n\t}\n\treturn placement;\n}\n\n/** Elevation angle (degrees, above the content's own horizontal plane) for a near-overhead ring view. */\nconst CAROUSEL_ELEVATION_DEG = 34;\n\n/**\n * World-space Y offset for the camera above the content's own centroid.\n *\n * `cycleSpatial` (see `smartart-3d-spatial.ts`) flattens every node onto\n * `y = 0`, arranging them on a ring in the XZ plane. The default modest\n * elevation (a good three-quarter tilt for extruded-flat and receding-tree\n * content, which both keep real vertical extent) views that flat ring\n * almost edge-on: nodes on the far side of the ring sit almost directly\n * behind the near ones from the camera's angle, so the diagram reads as a\n * couple of overlapping shapes instead of the circle the 2D layout shows.\n * A steep, near-overhead elevation (expressed as a fixed angle, not a\n * fraction of `radius`, so it holds regardless of the radius/distance\n * ratio) opens the ring up into a readable circle, matching the 2D\n * cycle/radial layout's own top-down gestalt.\n */\nexport function cameraElevation(\n\tfamily: SmartArt3DModel['family'],\n\tradius: number,\n\tdist: number,\n): number {\n\tif (family === 'cycle' || family === 'radial') {\n\t\treturn dist * Math.tan((CAROUSEL_ELEVATION_DEG * Math.PI) / 180);\n\t}\n\treturn radius * 0.3;\n}\n\n/**\n * Y-axis-only billboard: rotate every label plane to face the camera's\n * current azimuth, keeping the plane vertical (so captions never tilt or\n * flip upside down).\n *\n * A label's position floats just off its node's own (possibly rotated,\n * possibly radially-outward-facing) front face, but a plane rotated to\n * match that face is only readable when the camera happens to be roughly\n * in front of it. For a spatial/carousel arrangement (`cycleSpatial`),\n * that is true for at most the ~180 degrees of the ring facing the\n * camera, and only good for reading text within roughly +/-45 degrees of\n * dead-on; nodes further round the ring go edge-on (near-zero projected\n * width; `DoubleSide` on the material does not help, since that only\n * fixes a plane facing directly away, not sideways) or fully backward.\n * Re-orienting the plane itself toward the camera, every frame (so this\n * keeps working through an interactive orbit), makes every node's caption\n * legible regardless of which way its underlying block happens to face.\n */\nexport function billboardLabels(built: BuiltMeshGroup, camera: PerspectiveCamera): void {\n\tbuilt.forEachLabelPlane((plane: Mesh) => {\n\t\tconst dx = camera.position.x - plane.position.x;\n\t\tconst dz = camera.position.z - plane.position.z;\n\t\tplane.rotation.set(0, Math.atan2(dx, dz), 0);\n\t});\n}\n","/**\n * Three.js SmartArt renderer - vanilla scene runtime.\n *\n * Frames a {@link SmartArt3DModel} in a WebGL scene (lights, perspective\n * camera, optional OrbitControls, render loop) on a caller-provided canvas.\n * Pure vanilla three.js - no framework code - so the React, Vue, and Angular\n * bindings all mount it through a thin canvas wrapper. `three` is imported here\n * only; this module lives behind the `pptx-viewer-shared/smartart-3d` subpath so\n * it is lazily loaded and `three` stays an optional dependency.\n *\n * Camera framing (`contentSphere`/`fitCamera`/`cameraElevation`) and label\n * billboarding (`billboardLabels`) are pure geometry, split out to\n * `scene-camera.ts` to keep this module focused on mount/render-loop\n * orchestration.\n */\n\nimport {\n\tAmbientLight,\n\tColor,\n\tDirectionalLight,\n\tPerspectiveCamera,\n\tScene,\n\tWebGLRenderer,\n} from 'three';\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';\n\nimport type { TextStyleAnimationDescriptor } from '../render/animation-text-style-resolve';\nimport type { SmartArt3DModel } from '../render/smartart-3d-types';\nimport { buildMeshGroup } from './meshes';\nimport { billboardLabels, contentSphere, fitCamera, FOV } from './scene-camera';\n\n/** Tunables for the mounted 3D view. */\nexport interface SmartArt3DViewOptions {\n\t/** Enable OrbitControls (rotate/zoom). Default `false`. */\n\tinteractive?: boolean;\n\t/** Slowly auto-rotate the model. Default `false`. */\n\tautoRotate?: boolean;\n\t/** Solid background colour `#rrggbb`; omit for transparent. */\n\tbackground?: string;\n\t/** Device pixel-ratio cap. Default `2`. */\n\tmaxPixelRatio?: number;\n\t/** Active font-style emphasis override (bold/italic/underline/size/colour) for every node's caption. */\n\ttextStyle?: TextStyleAnimationDescriptor;\n}\n\n/** Imperative handle to a mounted SmartArt 3D view. */\nexport interface SmartArt3DHandle {\n\t/** Resize the renderer + camera to new pixel dimensions. */\n\tresize: (width: number, height: number) => void;\n\t/** Toggle interactive orbit controls at runtime. */\n\tsetInteractive: (on: boolean) => void;\n\t/** Apply (or clear) a font-style emphasis override on every node's caption. */\n\tsetTextStyle: (style: TextStyleAnimationDescriptor | undefined) => void;\n\t/** Tear down the renderer, controls, and all GPU resources. */\n\tdispose: () => void;\n}\n\n/**\n * Mount a SmartArt 3D model onto a canvas and start rendering.\n *\n * @returns a handle for resizing, toggling interactivity, and disposal.\n */\nexport function mountSmartArt3D(\n\tcanvas: HTMLCanvasElement,\n\tmodel: SmartArt3DModel,\n\twidth: number,\n\theight: number,\n\toptions: SmartArt3DViewOptions = {},\n): SmartArt3DHandle {\n\tconst renderer = new WebGLRenderer({ canvas, antialias: true, alpha: !options.background });\n\trenderer.setPixelRatio(\n\t\tMath.min(\n\t\t\ttypeof window === 'undefined' ? 1 : window.devicePixelRatio || 1,\n\t\t\toptions.maxPixelRatio ?? 2,\n\t\t),\n\t);\n\trenderer.setSize(width, height, false);\n\n\tconst scene = new Scene();\n\tif (options.background) {\n\t\tscene.background = new Color(options.background);\n\t}\n\n\tconst bounds = contentSphere(model);\n\tconst { cx, cy, cz, radius } = bounds;\n\tconst aspect = width / Math.max(1, height);\n\t// Fit the content's projected bounding box (not just its sphere) to the\n\t// frame; see `fitCamera` for why the sphere fit alone left wide, flat\n\t// diagrams tiny.\n\tconst placement = fitCamera(bounds, model.family, aspect);\n\tconst { dist } = placement;\n\n\tconst camera = new PerspectiveCamera(FOV, aspect, 0.1, dist * 8 + radius * 4);\n\tcamera.position.set(...placement.position);\n\tcamera.lookAt(...placement.target);\n\n\tscene.add(new AmbientLight(0xffffff, 0.62));\n\tconst key = new DirectionalLight(0xffffff, 0.95);\n\tkey.position.set(cx + radius, cy + radius * 1.4, cz + dist);\n\tscene.add(key);\n\tconst fill = new DirectionalLight(0xffffff, 0.3);\n\tfill.position.set(cx - radius, cy - radius * 0.6, cz + dist * 0.6);\n\tscene.add(fill);\n\n\tconst built = buildMeshGroup(model, options.textStyle);\n\tscene.add(built.group);\n\n\tlet controls: OrbitControls | null = null;\n\tconst enableControls = (on: boolean): void => {\n\t\tif (on && !controls) {\n\t\t\tcontrols = new OrbitControls(camera, canvas);\n\t\t\tcontrols.enablePan = false;\n\t\t\tcontrols.target.set(cx, cy, cz);\n\t\t\tcontrols.minDistance = dist * 0.4;\n\t\t\tcontrols.maxDistance = dist * 3;\n\t\t\tcontrols.update();\n\t\t} else if (!on && controls) {\n\t\t\tcontrols.dispose();\n\t\t\tcontrols = null;\n\t\t}\n\t\tif (controls) {\n\t\t\tcontrols.autoRotate = Boolean(options.autoRotate);\n\t\t\tcontrols.autoRotateSpeed = 1.2;\n\t\t}\n\t};\n\tenableControls(Boolean(options.interactive));\n\n\tlet frame = 0;\n\tlet disposed = false;\n\tconst renderLoop = (): void => {\n\t\tif (disposed) {\n\t\t\treturn;\n\t\t}\n\t\tframe = requestAnimationFrame(renderLoop);\n\t\tbillboardLabels(built, camera);\n\t\tcontrols?.update();\n\t\trenderer.render(scene, camera);\n\t};\n\tframe = requestAnimationFrame(renderLoop);\n\n\treturn {\n\t\tresize(w: number, h: number) {\n\t\t\tcamera.aspect = w / Math.max(1, h);\n\t\t\tcamera.updateProjectionMatrix();\n\t\t\trenderer.setSize(w, h, false);\n\t\t},\n\t\tsetInteractive(on: boolean) {\n\t\t\tenableControls(on);\n\t\t},\n\t\tsetTextStyle(style: TextStyleAnimationDescriptor | undefined) {\n\t\t\tbuilt.setTextStyle(style);\n\t\t},\n\t\tdispose() {\n\t\t\tdisposed = true;\n\t\t\tcancelAnimationFrame(frame);\n\t\t\tcontrols?.dispose();\n\t\t\tbuilt.dispose();\n\t\t\trenderer.dispose();\n\t\t},\n\t};\n}\n","/**\n * `pptx-viewer-shared/smartart-3d` - vanilla three.js SmartArt scene runtime.\n *\n * Lazily imported by each binding's SmartArt 3D wrapper so `three` stays an\n * optional dependency: when it is not installed the dynamic import rejects and\n * the binding falls back to the SVG `SmartArtRenderer`. The pure model builder\n * (`buildSmartArt3DModel`) and its types live in the main barrel\n * (`pptx-viewer-shared`) and should be imported from there directly.\n */\n\nexport { mountSmartArt3D } from './scene';\nexport type { SmartArt3DHandle, SmartArt3DViewOptions } from './scene';\nexport type {\n\tSmartArt3DModel,\n\tSmartArt3DModelOptions,\n\tSmartArt3DMesh,\n\tSmartArt3DConnector,\n} from '../render/smartart-3d-types';\n"],"names":[],"mappings":"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"}