pptx-angular-viewer 4.10.1 → 4.12.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.
@@ -3,7 +3,7 @@ import * as i0 from '@angular/core';
3
3
  import { InjectionToken, input, output, inject, computed, ChangeDetectionStrategy, Component, signal, Injectable, DestroyRef, viewChild, effect, Injector, afterNextRender, untracked, ElementRef, HostListener, Renderer2, ViewEncapsulation, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, afterRenderEffect, Input, Pipe, EventEmitter, Output, linkedSignal } from '@angular/core';
4
4
  import { TranslateService, TranslatePipe, translate } from '@ngx-translate/core';
5
5
  import { THEME_PRESETS, themeColorSchemesEqual, guideEmuToPx, hasTextProperties, evaluateCustomGeometryPathData, getShapeClipPath, getAdjustmentAwareClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, evaluateCustomGeometryPaths, customGeometryPathsToSvgSubpaths, evaluatePresetShape, hasShapeProperties, ooxmlGradientAngleToCssDegrees, getShapeType, isImageLikeElement as isImageLikeElement$1, getElementTextContent, getRoundRectRadiusPx as getRoundRectRadiusPx$1, applyDrawingColorTransforms as applyDrawingColorTransforms$1, hslToRgb as hslToRgb$1, PRESET_COLOR_MAP, getSubstituteFontFamily, parsePanoseString, evaluateGuides, deobfuscateFont, detectFontFormat, stripXmlOrderSuffix, orderedXmlKey, parseDataUrlToBytes, sampleFirstPixelColorFromBytes, flattenChartUserShapes, chartDataChangeType, isParetoChartData, resolveDisplayedChartTypeName, chartDataAddSeries, chartDataRemoveSeries, chartDataAddCategory, chartDataRemoveCategory, chartDataUpdatePoint, resolveThemeColorRef, resolveOoxmlPresetMapping, getCapturedPreset, resolveDirectionSubtype, capturedPresetNativeAnimation, timingCurveToAccelDecel, rebuildTableStructureInRawXml, updateCellTextInRawXml, deepCloneData, cloneTemplateElementsBySlideId, cloneSlide, MIN_ELEMENT_SIZE as MIN_ELEMENT_SIZE$1, EMU_PER_PX as EMU_PER_PX$6, DEFAULT_POWERPOINT_TABLE_STYLE_ID, DEFAULT_SCHEME_COLOR_MAP as DEFAULT_SCHEME_COLOR_MAP$1, formatAutoNumberMarker, createAutoNumberSequence, nextAutoNumber, breakAutoNumberRun, ELEMENT_PARAGRAPH_GEOMETRY_KEYS, getLinkedTextBoxSegments, deriveSlideTitle, svgPathToPolygons, THEME_COLOR_SCHEME_KEYS, createBuiltinVariables, resolveCoordinate, getPresetConnectionSites, getConnectorPathGeometry, resolveCustomGeometryToken, resolveCustomGeometryGuideContext, lookupPresetShape, normalizeStShapeType, PptxHandler, styleStroke, strokeFor, fitFontSize, nodeTextStyle, truncate, nodeOpacity, nodeStroke, nodeFill, styleShadow, buildTree, treeWidth, treeDepth, gearPoints, flattenNodes, interpretSmartArtLayout, smartArtQuickStyleHas3d, relayoutSmartArt, addSmartArtNode, removeSmartArtNode, demoteSmartArtNode, promoteSmartArtNode, reorderSmartArtNode, createEditorId, makeStoreAwareId, reassignDescendantIds, checkMissingAltText, checkMissingSlideTitle, checkLowContrast, checkComplexTables, checkBlankSlide, checkDuplicateTitles, createChartElement, createImageElement, THEME_COLOR_PALETTE_COLUMNS, hexToRgbChannels as hexToRgbChannels$2, rgbToHsl as rgbToHsl$1, renameSlideMaster, deleteSlideMaster, duplicateSlideMaster, insertSlideMaster, renameLayout, deleteLayout, duplicateLayout, insertLayout, detectFileFormat, themeColorRefFromColorChoice, cropShapeForPresetGeometry, cloneElement, duplicateElement, resolveModifyVerifierAlgorithmName, verifyModifyPassword, TABLE_STYLE_PART_SEQUENCE, douglasPeucker, serializePptxToJson, PPTX_JSON_MIME_TYPE, PptxMarkdownConverter, ThemePresets, formatCommentTimestamp as formatCommentTimestamp$1, EncryptedFileError, decodeXmlEntities, parseSignatureXml, groupElements, ungroupElements, updateSmartArtNodeText, setSmartArtNodeStyle, isInkElement, isZoomElement, SvgExporter, applyThemeToData, reResolveElementColors, pptxActionToElementAction, elementActionToPptxAction, setChartTitle, setChartLegend, setChartDataLabels, setChartAxis, setChartSeriesTrendline, setChartSeriesErrorBars, setChartDataPointLabel, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointMarker, setChartDataPointExplosion, getOleSheetGrid, getOleDocumentParagraphs, getOleNestedDeckDetail, applyOleSheetCellEdit, applyOleDocumentParagraphEdit, applyOleNestedDeckElementTextEdit, replaceOleFile, getOleObjectTypeLabel, addSmartArtNodeAsChild, switchSmartArtLayout, SWITCHABLE_LAYOUT_TYPES, normalizeTableStyleGuid, createTableStyleEntry, addTableStyleToMap, deleteTableStyleFromMap, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, applyThemeOverrideToSlide, TRANSITION_VALID_DIRECTIONS, TRANSITION_PATTERN_OPTIONS, TRANSITION_THRUBLK_TYPES } from 'pptx-viewer-core';
6
- import { parse } from 'opentype.js';
6
+ import * as opentypeModule from 'opentype.js';
7
7
  import DOMPurify from 'dompurify';
8
8
  import { z } from 'zod';
9
9
  import * as mcp from 'pptx-viewer-mcp';
@@ -387,13 +387,25 @@ function renderSelectMenu(menu, choices, value) {
387
387
  });
388
388
  menu.replaceChildren(...menuItems);
389
389
  }
390
- /** Keep the popup anchored below the trigger within the viewport. */
390
+ /** Prefer below the trigger, flipping above when it offers more room. */
391
391
  function positionSelectMenu(menu, trigger) {
392
392
  const rect = trigger.getBoundingClientRect();
393
- menu.style.minWidth = `${rect.width}px`;
394
- menu.style.maxHeight = `${Math.min(240, Math.max(0, window.innerHeight - rect.bottom - 8))}px`;
395
- menu.style.top = `${rect.bottom + 4}px`;
396
- menu.style.left = `${Math.max(0, Math.min(rect.left, window.innerWidth - menu.offsetWidth))}px`;
393
+ const margin = 8;
394
+ const gap = 4;
395
+ const width = Math.max(0, window.innerWidth - margin * 2);
396
+ const below = Math.max(0, window.innerHeight - rect.bottom - gap - margin);
397
+ const above = Math.max(0, rect.top - gap - margin);
398
+ menu.style.minWidth = `${Math.min(rect.width, width)}px`;
399
+ menu.style.maxWidth = `${width}px`;
400
+ // Measure at the normal height cap before choosing a side. Reset the cap
401
+ // on every reposition so a previously constrained menu can grow again.
402
+ menu.style.maxHeight = '240px';
403
+ const preferredHeight = menu.getBoundingClientRect().height;
404
+ const flip = below < preferredHeight && above > below;
405
+ menu.style.maxHeight = `${Math.min(240, flip ? above : below)}px`;
406
+ const { height, width: menuWidth } = menu.getBoundingClientRect();
407
+ menu.style.top = `${Math.max(margin, flip ? rect.top - gap - height : rect.bottom + gap)}px`;
408
+ menu.style.left = `${Math.max(margin, Math.min(rect.left, window.innerWidth - menuWidth - margin))}px`;
397
409
  }
398
410
  /** Expose the keyboard target while keeping the selected state separate. */
399
411
  function markSelectActive(menu, trigger, active, open) {
@@ -693,16 +705,18 @@ function definePptxSelect(registry) {
693
705
  this.text.textContent =
694
706
  this.choices.find((choice) => choice.value === this.value)?.label ?? '';
695
707
  this.trigger.disabled = this.disabled;
708
+ if (this.disabled) {
709
+ this.close();
710
+ }
696
711
  this.trigger.setAttribute('aria-label', this.getAttribute('aria-label') ?? '');
697
712
  this.internals?.setFormValue?.(this.disabled ? null : this.value);
698
- // Build the popup lazily when an inspector select opens.
699
713
  if (this.open || this.menu.childElementCount) {
700
714
  this.renderMenu();
701
715
  }
702
716
  }
703
717
  renderMenu() {
704
718
  renderSelectMenu(this.menu, this.choices, this.value);
705
- this.markActive();
719
+ markSelectActive(this.menu, this.trigger, this.active, this.open);
706
720
  }
707
721
  show() {
708
722
  if (this.disabled ||
@@ -710,10 +724,6 @@ function definePptxSelect(registry) {
710
724
  this.open) {
711
725
  return;
712
726
  }
713
- const availableBelow = window.innerHeight - this.trigger.getBoundingClientRect().bottom;
714
- if (availableBelow < Math.min(this.choices.length * 32 + 12, 160)) {
715
- this.trigger.scrollIntoView?.({ block: 'center' });
716
- }
717
727
  this.open = true;
718
728
  this.setAttribute('open', '');
719
729
  const selected = this.choices.findIndex((choice) => choice.value === this.value && !choice.disabled && !choice.hidden);
@@ -730,7 +740,7 @@ function definePptxSelect(registry) {
730
740
  }
731
741
  this.trigger.setAttribute('aria-expanded', 'true');
732
742
  this.position();
733
- this.markActive();
743
+ markSelectActive(this.menu, this.trigger, this.active, this.open);
734
744
  document.addEventListener('pointerdown', this.onOutside, true);
735
745
  window.addEventListener('resize', this.position);
736
746
  window.addEventListener('scroll', this.position, true);
@@ -740,6 +750,8 @@ function definePptxSelect(registry) {
740
750
  return;
741
751
  }
742
752
  this.open = false;
753
+ window.clearTimeout(this.searchTimer);
754
+ this.search = '';
743
755
  this.removeAttribute('open');
744
756
  if (this.menu.matches(':popover-open')) {
745
757
  this.menu.hidePopover();
@@ -751,9 +763,6 @@ function definePptxSelect(registry) {
751
763
  window.removeEventListener('resize', this.position);
752
764
  window.removeEventListener('scroll', this.position, true);
753
765
  }
754
- markActive() {
755
- markSelectActive(this.menu, this.trigger, this.active, this.open);
756
- }
757
766
  move(step) {
758
767
  if (!this.open) {
759
768
  this.show();
@@ -762,7 +771,7 @@ function definePptxSelect(registry) {
762
771
  return;
763
772
  }
764
773
  this.active = nextSelectActive(this.choices, this.active, step);
765
- this.markActive();
774
+ markSelectActive(this.menu, this.trigger, this.active, this.open);
766
775
  }
767
776
  commit(index) {
768
777
  const choice = this.choices[index];
@@ -793,11 +802,9 @@ function definePptxSelect(registry) {
793
802
  this.show();
794
803
  this.active =
795
804
  event.key === 'Home'
796
- ? this.choices.findIndex((choice) => !choice.disabled && !choice.hidden)
797
- : this.choices.length -
798
- 1 -
799
- [...this.choices].reverse().findIndex((choice) => !choice.disabled && !choice.hidden);
800
- this.markActive();
805
+ ? nextSelectActive(this.choices, -1, 1)
806
+ : nextSelectActive(this.choices, 0, -1);
807
+ markSelectActive(this.menu, this.trigger, this.active, this.open);
801
808
  }
802
809
  else if (event.key === 'Enter' || event.key === ' ') {
803
810
  event.preventDefault();
@@ -815,6 +822,9 @@ function definePptxSelect(registry) {
815
822
  this.close();
816
823
  this.trigger.focus();
817
824
  }
825
+ else if (event.key === 'Tab') {
826
+ this.close();
827
+ }
818
828
  else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
819
829
  event.stopPropagation();
820
830
  this.search += event.key.toLocaleLowerCase();
@@ -828,7 +838,7 @@ function definePptxSelect(registry) {
828
838
  choice.label.toLocaleLowerCase().startsWith(this.search));
829
839
  if (index >= 0) {
830
840
  this.active = index;
831
- this.markActive();
841
+ markSelectActive(this.menu, this.trigger, this.active, this.open);
832
842
  }
833
843
  }
834
844
  }
@@ -6513,7 +6523,7 @@ function renderedImageCorrections(effects) {
6513
6523
  };
6514
6524
  }
6515
6525
  /** Clamp a number to the inclusive `[lo, hi]` range. */
6516
- function clamp$a(v, lo, hi) {
6526
+ function clamp$9(v, lo, hi) {
6517
6527
  return v < lo ? lo : v > hi ? hi : v;
6518
6528
  }
6519
6529
  /** Neutral colour temperature, in Kelvin (`a14:colorTemperature/@colorTemp`). */
@@ -6532,7 +6542,7 @@ function buildColorTemperatureCss(colorTempK) {
6532
6542
  if (diff === 0) {
6533
6543
  return undefined;
6534
6544
  }
6535
- const strength = clamp$a(Math.abs(diff) / COLOR_TEMP_RANGE_K, 0, 1);
6545
+ const strength = clamp$9(Math.abs(diff) / COLOR_TEMP_RANGE_K, 0, 1);
6536
6546
  if (diff < 0) {
6537
6547
  // Warmer: sepia + a small negative hue-rotate biases the sepia toward
6538
6548
  // orange rather than its default brown.
@@ -6581,7 +6591,7 @@ function getImageCorrectionsFilterTokens(imageEffects) {
6581
6591
  // Soften: a small CSS blur. -100000 (fully softened) maps to a 3px blur,
6582
6592
  // a deliberately gentle ceiling since this runs on top of the picture's
6583
6593
  // full-resolution bitmap rather than a thumbnail.
6584
- const strength = clamp$a(Math.abs(sharpenSoftenAmount) / 100000, 0, 1);
6594
+ const strength = clamp$9(Math.abs(sharpenSoftenAmount) / 100000, 0, 1);
6585
6595
  tokens.push(`blur(${(strength * 3).toFixed(2)}px)`);
6586
6596
  }
6587
6597
  return tokens;
@@ -6612,18 +6622,18 @@ function getImageSharpenFilter(effects, elementId) {
6612
6622
  if (typeof amount !== 'number' || amount <= 0) {
6613
6623
  return undefined;
6614
6624
  }
6615
- const strength = clamp$a(amount / 100000, 0, 1);
6625
+ const strength = clamp$9(amount / 100000, 0, 1);
6616
6626
  const id = getImageSharpenFilterId(elementId);
6617
6627
  const filterMarkup = `<feConvolveMatrix order="3" kernelMatrix="${buildSharpenKernel(strength)}" preserveAlpha="true"/>`;
6618
6628
  return { id, cssReference: `url(#${id})`, filterMarkup };
6619
6629
  }
6620
6630
 
6621
- function clamp$9(value, minimum, maximum) {
6631
+ function clamp$8(value, minimum, maximum) {
6622
6632
  return Math.min(maximum, Math.max(minimum, value));
6623
6633
  }
6624
6634
  /** Build an SVG discrete-transfer table for a 0 through 100 percent threshold. */
6625
6635
  function buildImageBiLevelTable(threshold) {
6626
- const cutoff = clamp$9(threshold, 0, 100);
6636
+ const cutoff = clamp$8(threshold, 0, 100);
6627
6637
  return Array.from({ length: 101 }, (_, index) => (index >= cutoff ? '1' : '0')).join(' ');
6628
6638
  }
6629
6639
  /**
@@ -6631,7 +6641,7 @@ function buildImageBiLevelTable(threshold) {
6631
6641
  * Positive values move channels toward white; negative values move them toward black.
6632
6642
  */
6633
6643
  function buildImageLuminanceTransfer(amount) {
6634
- const normalized = clamp$9(amount / 100, -1, 1);
6644
+ const normalized = clamp$8(amount / 100, -1, 1);
6635
6645
  return normalized >= 0
6636
6646
  ? { slope: 1 - normalized, intercept: normalized }
6637
6647
  : { slope: 1 + normalized, intercept: 0 };
@@ -6805,12 +6815,12 @@ function getImageFillOverlayFilter(element, elementId = isImageLikeElement$1(ele
6805
6815
 
6806
6816
  // ── Internal helpers ────────────────────────────────────────────────────────
6807
6817
  /** Clamp a number to the inclusive `[lo, hi]` range. */
6808
- function clamp$8(v, lo, hi) {
6818
+ function clamp$7(v, lo, hi) {
6809
6819
  return v < lo ? lo : v > hi ? hi : v;
6810
6820
  }
6811
6821
  /** Normalize a radius value (0–100) to a 0–1 float for proportional scaling. */
6812
6822
  function normalizeRadius01(radius) {
6813
- return clamp$8(radius / 100, 0, 1);
6823
+ return clamp$7(radius / 100, 0, 1);
6814
6824
  }
6815
6825
  /** Get the image effects off an element, or `undefined` for non-image elements. */
6816
6826
  function getEffects(element) {
@@ -7089,7 +7099,7 @@ function getImageEffectsOpacity(element) {
7089
7099
  return undefined;
7090
7100
  }
7091
7101
  if (typeof effects.alphaModFix === 'number') {
7092
- return clamp$8(effects.alphaModFix / 100, 0, 1);
7102
+ return clamp$7(effects.alphaModFix / 100, 0, 1);
7093
7103
  }
7094
7104
  return undefined;
7095
7105
  }
@@ -7158,7 +7168,7 @@ function buildImageAlphaFilterMarkup(effects) {
7158
7168
  // a:cont container (see PptxImageEffects.alphaMod.amt). Distinct from the
7159
7169
  // sibling alphaModFix effect (handled via CSS opacity, not here).
7160
7170
  if (typeof effects.alphaMod?.amt === 'number') {
7161
- const mul = clamp$8(effects.alphaMod.amt / 100, 0, 1);
7171
+ const mul = clamp$7(effects.alphaMod.amt / 100, 0, 1);
7162
7172
  next((inp, out) => `<feComponentTransfer in="${inp}" result="${out}">` +
7163
7173
  `<feFuncA type="linear" slope="${mul}" intercept="0"/>` +
7164
7174
  '</feComponentTransfer>');
@@ -7179,13 +7189,13 @@ function buildImageAlphaFilterMarkup(effects) {
7179
7189
  '</feComponentTransfer>');
7180
7190
  }
7181
7191
  if (typeof effects.alphaRepl === 'number') {
7182
- const a = clamp$8(effects.alphaRepl / 100, 0, 1);
7192
+ const a = clamp$7(effects.alphaRepl / 100, 0, 1);
7183
7193
  next((inp, out) => `<feComponentTransfer in="${inp}" result="${out}">` +
7184
7194
  `<feFuncA type="linear" slope="0" intercept="${a}"/>` +
7185
7195
  '</feComponentTransfer>');
7186
7196
  }
7187
7197
  if (typeof effects.alphaBiLevel === 'number') {
7188
- const t = clamp$8(effects.alphaBiLevel / 100, 0, 1);
7198
+ const t = clamp$7(effects.alphaBiLevel / 100, 0, 1);
7189
7199
  const table = Array.from({ length: 10 }, (_, i) => (i / 10 >= t ? '1' : '0')).join(' ');
7190
7200
  next((inp, out) => `<feComponentTransfer in="${inp}" result="${out}">` +
7191
7201
  `<feFuncA type="discrete" tableValues="${table}"/>` +
@@ -7228,7 +7238,7 @@ function buildImageAlphaFilterMarkup(effects) {
7228
7238
  '</feComponentTransfer>');
7229
7239
  }
7230
7240
  if (effects.hsl && typeof effects.hsl.sat === 'number') {
7231
- const v = clamp$8(1 + effects.hsl.sat / 100, 0, 2);
7241
+ const v = clamp$7(1 + effects.hsl.sat / 100, 0, 2);
7232
7242
  next((inp, out) => `<feColorMatrix in="${inp}" result="${out}" type="saturate" values="${v}"/>`);
7233
7243
  }
7234
7244
  if (effects.hsl && typeof effects.hsl.lum === 'number') {
@@ -7297,7 +7307,7 @@ function buildDiscreteTable(steps) {
7297
7307
  * don't need an SVG filter (handled via CSS instead).
7298
7308
  */
7299
7309
  function buildArtisticFilterMarkup(effectName, radius) {
7300
- const normalizedRadius = clamp$8(radius, 0, 100) / 100;
7310
+ const normalizedRadius = clamp$7(radius, 0, 100) / 100;
7301
7311
  switch (effectName) {
7302
7312
  case 'artisticFilmGrain':
7303
7313
  case 'filmGrain': {
@@ -9959,6 +9969,15 @@ function buildEmbeddedFontStyles(fonts, mintObjectUrl) {
9959
9969
  return { fontFaceCss, fontFamilies, objectUrls };
9960
9970
  }
9961
9971
 
9972
+ /** Resolve opentype.js as either its ESM namespace or its CommonJS default object. */
9973
+ function parseOpenTypeFont(buffer, module = opentypeModule) {
9974
+ const parser = module.parse ?? module.default?.parse;
9975
+ if (!parser) {
9976
+ throw new Error('opentype.js does not expose a font parser');
9977
+ }
9978
+ return parser(buffer);
9979
+ }
9980
+
9962
9981
  function toArrayBuffer(bytes) {
9963
9982
  // `opentype.parse` wants a real ArrayBuffer; `bytes` may be a view over a
9964
9983
  // larger buffer (e.g. `rawFontData` sliced from a ZIP entry), so this
@@ -10028,7 +10047,7 @@ class GlyphOutlineFontCache {
10028
10047
  continue;
10029
10048
  }
10030
10049
  try {
10031
- this.fonts.set(key, parse(toArrayBuffer(bytes)));
10050
+ this.fonts.set(key, parseOpenTypeFont(toArrayBuffer(bytes)));
10032
10051
  }
10033
10052
  catch {
10034
10053
  // Unparseable font (corrupt or an outline format opentype.js does
@@ -10049,7 +10068,7 @@ class GlyphOutlineFontCache {
10049
10068
  return false;
10050
10069
  }
10051
10070
  try {
10052
- this.fonts.set(key, parse(toArrayBuffer(bytes)));
10071
+ this.fonts.set(key, parseOpenTypeFont(toArrayBuffer(bytes)));
10053
10072
  return true;
10054
10073
  }
10055
10074
  catch {
@@ -19801,7 +19820,7 @@ function buildAreas(chartData, catCount, layout, range, sourceIndices, xPosition
19801
19820
  return { primitives: [...primitives, ...labelBoxes], dataLabels };
19802
19821
  }
19803
19822
 
19804
- function clamp$7(value, min, max) {
19823
+ function clamp$6(value, min, max) {
19805
19824
  return Math.min(Math.max(value, min), max);
19806
19825
  }
19807
19826
  /** Resolve where the horizontal category/date axis crosses its linked value axis. */
@@ -19814,7 +19833,7 @@ function horizontalAxisY(valueAxis, range, layout, fallback) {
19814
19833
  }
19815
19834
  const crossingValue = valueAxis?.crossesAt ?? 0;
19816
19835
  if (valueAxis?.crossesAt !== undefined || valueAxis?.crosses === 'autoZero') {
19817
- return valueToY(clamp$7(crossingValue, range.min, range.max), range, layout.plotTop, layout.plotBottom);
19836
+ return valueToY(clamp$6(crossingValue, range.min, range.max), range, layout.plotTop, layout.plotBottom);
19818
19837
  }
19819
19838
  return fallback === 'top' ? layout.plotTop : layout.plotBottom;
19820
19839
  }
@@ -19830,10 +19849,10 @@ function verticalAxisX(categoryAxis, categoryCount, layout, fallback, dateValues
19830
19849
  if (categoryAxis.axisType === 'dateAx' && dateValues?.length) {
19831
19850
  const min = categoryAxis.min ?? Math.min(...dateValues);
19832
19851
  const max = categoryAxis.max ?? Math.max(...dateValues);
19833
- const ratio = max === min ? 0.5 : (clamp$7(categoryAxis.crossesAt, min, max) - min) / (max - min);
19852
+ const ratio = max === min ? 0.5 : (clamp$6(categoryAxis.crossesAt, min, max) - min) / (max - min);
19834
19853
  return layout.plotLeft + ratio * layout.plotWidth;
19835
19854
  }
19836
- const index = clamp$7(categoryAxis.crossesAt - 1, 0, Math.max(categoryCount - 1, 0));
19855
+ const index = clamp$6(categoryAxis.crossesAt - 1, 0, Math.max(categoryCount - 1, 0));
19837
19856
  const ratio = categoryCount <= 1 ? 0.5 : index / (categoryCount - 1);
19838
19857
  return layout.plotLeft + ratio * layout.plotWidth;
19839
19858
  }
@@ -19948,7 +19967,7 @@ function buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, cat
19948
19967
  }
19949
19968
 
19950
19969
  /** Per-category absolute totals (for percentStacked normalisation). */
19951
- function categoryTotals$2(series, catCount) {
19970
+ function categoryTotals$1(series, catCount) {
19952
19971
  return Array.from({ length: catCount }, (_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0));
19953
19972
  }
19954
19973
  /**
@@ -19961,7 +19980,7 @@ function buildPercentStackedBars(chartData, catCount, layout, primaryRange, sour
19961
19980
  const primitives = [], dataLabels = [], series = chartData.series, palette = chartData.colorPalette, showLabels = chartData.style?.hasDataLabels, barGroupWidth = layout.plotWidth / Math.max(catCount, 1), barW = barGroupWidth * 0.6, barOffset = (barGroupWidth - barW) / 2, displaySeries = series.map((entry) => ({
19962
19981
  ...entry,
19963
19982
  values: sourceIndices.map((sourceIndex) => entry.values[sourceIndex] ?? 0),
19964
- })), totals = categoryTotals$2(displaySeries, catCount);
19983
+ })), totals = categoryTotals$1(displaySeries, catCount);
19965
19984
  for (let ci = 0; ci < catCount; ci++) {
19966
19985
  let posRunning = 0, negRunning = 0;
19967
19986
  const catTotal = totals[ci] || 1;
@@ -21423,7 +21442,7 @@ function widestCategoryLabelWidth(categoryLabels, fontSize) {
21423
21442
  return categoryLabels.reduce((max, label) => Math.max(max, estimateTextWidth(label, fontSize)), 0);
21424
21443
  }
21425
21444
  /** Per-category absolute totals (for percentStacked normalisation). */
21426
- function categoryTotals$1(series, catCount) {
21445
+ function categoryTotals(series, catCount) {
21427
21446
  return Array.from({ length: catCount }, (_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0));
21428
21447
  }
21429
21448
  /** Resolve the fill for one bar, honouring dPt overrides and varyColors. */
@@ -21509,7 +21528,7 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
21509
21528
  }
21510
21529
  }
21511
21530
  else {
21512
- const totals = isPercent ? categoryTotals$1(series, catCount) : [], barH = band * (isPercent ? 0.6 : 0.7), barOffset = (band - barH) / 2;
21531
+ const totals = isPercent ? categoryTotals(series, catCount) : [], barH = band * (isPercent ? 0.6 : 0.7), barOffset = (band - barH) / 2;
21513
21532
  for (let ci = 0; ci < catCount; ci++) {
21514
21533
  let posRunning = 0, negRunning = 0;
21515
21534
  const catTotal = isPercent ? totals[ci] || 1 : 1;
@@ -26429,7 +26448,7 @@ const RADAR_RINGS = 4, RADAR_RING_COLOR = '#cbd5e1', RADAR_SPOKE_COLOR = '#94a3b
26429
26448
  // COM (callouts-com.pptx): with no c:dLblPos, PowerPoint centres a radar
26430
26449
  // label on the spoke, this many label font sizes out past its point.
26431
26450
  RADAR_LABEL_RADIAL_EM = 3.5;
26432
- const clamp$6 = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
26451
+ const clamp$5 = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
26433
26452
  /**
26434
26453
  * Build the view-model for a radar / spider chart. Polar, so it has no
26435
26454
  * cartesian gridlines/axes; ring + spoke geometry and the data polygons all
@@ -26525,8 +26544,8 @@ function buildRadarViewModel(element, chartData, categoryLabels) {
26525
26544
  const angle = radarAngle(vi, catCount), out = RADAR_LABEL_RADIAL_EM * fontSize,
26526
26545
  // Kept inside the chart box, as PowerPoint keeps every label.
26527
26546
  auto = {
26528
- x: clamp$6(p.x + out * Math.cos(angle), fontSize, frame.width - fontSize),
26529
- y: clamp$6(p.y + out * Math.sin(angle), fontSize, frame.height - fontSize),
26547
+ x: clamp$5(p.x + out * Math.cos(angle), fontSize, frame.width - fontSize),
26548
+ y: clamp$5(p.y + out * Math.sin(angle), fontSize, frame.height - fontSize),
26530
26549
  };
26531
26550
  anchor = {
26532
26551
  ...applyLabelManualLayout(findPointLabel(series, vi)?.layout, frame, auto),
@@ -87456,617 +87475,59 @@ function buildSmartArt3DSpecForElement(element, options = {}) {
87456
87475
  }
87457
87476
 
87458
87477
  /**
87459
- * Pure geometry helpers for an interactive 3D CARTESIAN chart scene (category
87460
- * axis x depth/series axis x value axis), three-agnostic so the row/col/camera
87461
- * maths is unit-testable without mocking WebGL.
87462
- *
87463
- * Generalises the surface chart's grid/camera conventions
87464
- * ({@link ./surface-chart-3d-geom.ts}) for a plot whose Z (depth) axis is the
87465
- * SERIES axis rather than a second value dimension, and whose depth extent is
87466
- * driven by the authored `c:view3D/@depthPercent` (percentage of the plot's
87467
- * own width) rather than 1:1 with the row count. `bar3D`
87468
- * ({@link ./bar-chart-3d-scene.ts}) is the first consumer; `line3D`/`area3D`
87469
- * are expected to reuse this module unchanged, only swapping the mesh built
87470
- * from the resulting grid/camera placement.
87471
- *
87472
- * @module cartesian-chart-3d-geom
87473
- */
87474
- /** Half-unit spacing between adjacent category slots, in world units. Matches
87475
- * the surface chart's `GRID_SPACING` so the two scene kinds share one visual
87476
- * scale. */
87477
- const GRID_SPACING$1 = 0.5;
87478
- /** World-space height of the tallest data point (value axis extent). */
87479
- const MAX_VALUE_HEIGHT = 1.5;
87480
- /**
87481
- * Remap a point built in the "vertical" cartesian local frame (category =
87482
- * world X, value = world Y, series = world Z) onto the frame a horizontal
87483
- * `c:barDir val="bar"` `bar3D` chart needs (value = world X, category = world
87484
- * Y, series unchanged on Z): a -90 degree rotation about Z, i.e. `(x, y, z) ->
87485
- * (y, -x, z)`. The negation keeps category index 0 at the TOP of the scene
87486
- * (positive world Y), matching the flat SVG horizontal-bar engine's own
87487
- * top-to-bottom category order ({@link ./chart-horizontal-bars.ts}) so the
87488
- * true-3D scene and its 2D fallback never disagree about layout.
87489
- *
87490
- * Every "vertical-frame" point this cartesian geometry module produces (box
87491
- * centers, label anchors) can go through this same function, so a mesh
87492
- * rotated `-Math.PI / 2` about Z (see `bar-chart-3d-geometry.ts`) lines up
87493
- * with everything else built from the SAME local coordinates without a
87494
- * second, divergent set of horizontal-only layout maths.
87495
- */
87496
- function transposeForHorizontalBar3D(point) {
87497
- return [point[1], -point[0], point[2]];
87498
- }
87499
- /**
87500
- * Compute the world-space grid extent: `gridWidth` scales with the category
87501
- * count exactly like the surface chart's column axis; `gridDepth` is the
87502
- * chart's authored depth as a percentage of that width
87503
- * (`c:view3D/@depthPercent`, ECMA-376 default 100), NOT a per-series 1:1
87504
- * spacing, matching the OOXML semantics ("chart depth as a percentage of base
87505
- * width"). `rows` still gates the minimum depth so a single-series chart
87506
- * keeps a visible sliver of depth instead of collapsing to a flat plane.
87507
- */
87508
- function computeCartesianGridExtent(cols, rows, depthPercent) {
87509
- const gridWidth = Math.max(cols - 1, 1) * GRID_SPACING$1;
87510
- const depthScale = Math.min(Math.max((depthPercent ?? 100) / 100, 0.05), 3);
87511
- const gridDepth = Math.max(gridWidth * depthScale, rows > 1 ? GRID_SPACING$1 : GRID_SPACING$1 * 0.5);
87512
- return { gridWidth, gridDepth };
87513
- }
87514
- const DEFAULT_ELEVATION_DEG$1 = 15;
87515
- const DEFAULT_AZIMUTH_DEG$1 = 20;
87516
- const DEFAULT_PERSPECTIVE_DEG$1 = 30;
87517
- const FOV_MIN$1 = 15;
87518
- const FOV_MAX$1 = 75;
87519
- /** Near-orthographic FOV used when `rAngAx` (right-angle axes) is authored. */
87520
- const RIGHT_ANGLE_FOV = 20;
87521
- /** `c:view3D/c:perspective` is documented 0-240 degrees; PowerPoint's own UI
87522
- * caps the picker at 120, so anything past that is treated as "very deep". */
87523
- const PERSPECTIVE_MAX_DEG$1 = 120;
87524
- function clamp$5(value, min, max) {
87525
- return Math.min(Math.max(value, min), max);
87526
- }
87527
- function normalizeAzimuth$1(deg) {
87528
- const wrapped = deg % 360;
87529
- return wrapped < 0 ? wrapped + 360 : wrapped;
87530
- }
87531
- /**
87532
- * Resolve the camera's vertical field of view from `c:view3D`. `rAngAx`
87533
- * (right-angle axes) asks PowerPoint to drop true perspective, so this maps
87534
- * it to a narrow, near-orthographic FOV; otherwise `perspective` (0-120
87535
- * degrees in practice) scales linearly onto a `[FOV_MIN, FOV_MAX]` degree
87536
- * range. Not measured against PowerPoint's own COM-rendered output: this is a
87537
- * reasonable, documented approximation, not a claimed exact match.
87538
- */
87539
- function resolveCartesianCameraFov(view3D) {
87540
- if (view3D?.rAngAx) {
87541
- return RIGHT_ANGLE_FOV;
87542
- }
87543
- const perspective = clamp$5(view3D?.rperspective ?? DEFAULT_PERSPECTIVE_DEG$1, 0, PERSPECTIVE_MAX_DEG$1);
87544
- return FOV_MIN$1 + (perspective / PERSPECTIVE_MAX_DEG$1) * (FOV_MAX$1 - FOV_MIN$1);
87545
- }
87546
- /**
87547
- * Camera placement that frames a scene of world-space bounding radius
87548
- * `maxExtent`, looking at `[0, targetY, 0]`.
87549
- *
87550
- * The camera sits on a sphere around that target at the elevation/azimuth
87551
- * given by `rotX`/`rotY` (PowerPoint-like defaults when either is absent,
87552
- * matching the flat oblique-projection engine's own defaults so an untagged
87553
- * 3D chart looks consistent across presentations), at a distance derived
87554
- * from the resolved FOV so the scene's bounding sphere stays framed
87555
- * regardless of how narrow (near-orthographic) or wide the perspective is.
87556
- *
87557
- * `rotX` is clamped to `(-90, 90)` exclusive: exactly on the vertical axis is
87558
- * a degenerate `lookAt`/OrbitControls case (an ill-defined up vector).
87559
- *
87560
- * Pure sphere-placement math, extracted out of
87561
- * {@link computeCartesianCameraPlacement} so a non-cartesian 3D chart scene
87562
- * (no category/series grid to speak of) can reuse the identical
87563
- * camera-placement approach instead of duplicating it. `pie3D`
87564
- * ({@link ../pie-chart-3d-geom.ts}, `computePieChart3DCameraPlacement`) is
87565
- * the first such consumer.
87566
- */
87567
- function computeSphericalCameraPlacement(maxExtent, targetY, view3D) {
87568
- const fov = resolveCartesianCameraFov(view3D);
87569
- const halfFovRad = (fov * Math.PI) / 360;
87570
- // Distance for the scene's bounding radius to just fit the vertical FOV, plus margin.
87571
- const radius = (Math.max(maxExtent, 0.01) / Math.tan(halfFovRad)) * 0.75;
87572
- const target = [0, targetY, 0];
87573
- const elevationDeg = clamp$5(view3D?.rotX ?? DEFAULT_ELEVATION_DEG$1, -89, 89);
87574
- const azimuthDeg = normalizeAzimuth$1(view3D?.rotY ?? DEFAULT_AZIMUTH_DEG$1);
87575
- // Polar angle measured from the +Y axis; spherical -> cartesian.
87576
- const phi = ((90 - elevationDeg) * Math.PI) / 180;
87577
- const theta = (azimuthDeg * Math.PI) / 180;
87578
- const sinPhi = Math.sin(phi);
87579
- return {
87580
- position: [
87581
- radius * sinPhi * Math.sin(theta),
87582
- radius * Math.cos(phi) + target[1],
87583
- radius * sinPhi * Math.cos(theta),
87584
- ],
87585
- target,
87586
- fov,
87587
- };
87588
- }
87589
- /**
87590
- * Camera placement that frames the whole cartesian grid. Thin wrapper around
87591
- * {@link computeSphericalCameraPlacement} with the cartesian grid's own
87592
- * extent + look-at height.
87593
- *
87594
- * `horizontal` (a `c:barDir val="bar"` `bar3D` chart) mounts its boxes
87595
- * remapped into the transposed frame ({@link transposeForHorizontalBar3D}:
87596
- * value = world X, category = world Y instead of the vertical frame's
87597
- * category = X, value = Y). Framing those boxes with the UNTRANSPOSED camera
87598
- * placement pointed the look-at target at `[0, someHeight, 0]`, a point that
87599
- * is only near the rendered content in the vertical frame: once transposed,
87600
- * the value axis starts at world X = 0 and runs only in the POSITIVE
87601
- * direction (never centred on 0), so the untransposed target sat off to one
87602
- * side of the actual box cluster, cropping it hard against one edge of the
87603
- * frame and pushing the axis-label overlay (projected through the same
87604
- * camera) out of the view frustum entirely. Applying the identical rigid
87605
- * transform to both the computed position and target re-centres the camera
87606
- * on the actual (rotated) content without re-deriving the elevation/azimuth
87607
- * maths: rotating a camera and its look-at target by the same transform that
87608
- * rotated the scene preserves the viewing distance and angle. World "up"
87609
- * intentionally stays untouched (`camera.up` default `+Y`): the floor and
87610
- * `c:floor`/`c:sideWall`/`c:backWall` panels are never reoriented for
87611
- * `horizontal` either (see `bar-chart-3d-scene.ts`), so only the camera's
87612
- * position/target move, not its up vector.
87613
- */
87614
- function computeCartesianCameraPlacement(cols, rows, view3D, horizontal = false) {
87615
- const { gridWidth, gridDepth } = computeCartesianGridExtent(cols, rows, view3D?.depthPercent);
87616
- const maxExtent = Math.max(gridWidth, gridDepth, MAX_VALUE_HEIGHT);
87617
- const placement = computeSphericalCameraPlacement(maxExtent, MAX_VALUE_HEIGHT * 0.2, view3D);
87618
- if (!horizontal) {
87619
- return placement;
87620
- }
87621
- return {
87622
- ...placement,
87623
- position: transposeForHorizontalBar3D(placement.position),
87624
- target: transposeForHorizontalBar3D(placement.target),
87625
- };
87626
- }
87627
- /**
87628
- * Build the axis labels (category along the front edge, series along the
87629
- * right edge) as world-anchored {@link SurfaceLabel}s, reusing the surface
87630
- * chart's label type + DOM overlay ({@link ./surface-chart-3d-label-overlay.ts})
87631
- * unchanged. Thinned to at most `maxCat`/`maxSer` entries to avoid clutter,
87632
- * mirroring `buildSurfaceLabels`.
87633
- */
87634
- function buildCartesianChart3DLabels(cols, rows, categoryLabels, seriesNames, depthPercent, maxCat = 8, maxSer = 6, horizontal = false) {
87635
- const { gridWidth, gridDepth } = computeCartesianGridExtent(cols, rows, depthPercent);
87636
- const labels = [];
87637
- const remap = (anchor) => horizontal ? transposeForHorizontalBar3D(anchor) : anchor;
87638
- const catStep = Math.max(1, Math.ceil(categoryLabels.length / maxCat));
87639
- for (let i = 0; i < categoryLabels.length; i += catStep) {
87640
- const x = -gridWidth / 2 + (i / Math.max(cols - 1, 1)) * gridWidth;
87641
- labels.push({
87642
- key: `cat-${i}`,
87643
- text: categoryLabels[i],
87644
- anchor: remap([x, -0.15, gridDepth / 2 + 0.25]),
87645
- axis: 'category',
87646
- });
87647
- }
87648
- const serStep = Math.max(1, Math.ceil(seriesNames.length / maxSer));
87649
- for (let i = 0; i < seriesNames.length; i += serStep) {
87650
- const z = -gridDepth / 2 + (i / Math.max(rows - 1, 1)) * gridDepth;
87651
- labels.push({
87652
- key: `ser-${i}`,
87653
- text: seriesNames[i],
87654
- anchor: remap([gridWidth / 2 + 0.3, -0.15, z]),
87655
- axis: 'series',
87656
- });
87657
- }
87658
- return labels;
87659
- }
87660
-
87661
- /**
87662
- * Pure path-layout maths for the interactive `line3D` and `area3D` scenes
87663
- * ({@link ./line-chart-3d-scene.ts}, {@link ./area-chart-3d-scene.ts}).
87664
- *
87665
- * PowerPoint's real 3-D Line and 3-D Area chart types give every series its
87666
- * own depth ("Z") plane along the series axis, exactly like a clustered
87667
- * bar3D column (see {@link ./bar-chart-3d-layout.ts}) - unlike this engine's
87668
- * flat oblique-projection line3D/area3D fallback ({@link ./chart-3d-depth.ts}),
87669
- * which only offsets the whole chart by a single shared depth vector, with no
87670
- * per-series stagger. Also unlike bar3D, PowerPoint's chart-type gallery
87671
- * offers no "stacked 3-D Line"/"stacked 3-D Area", so every series always
87672
- * plots its OWN authored values on its OWN plane, never a running sum.
87673
- *
87674
- * X (category axis) uses the exact same anchor formula as
87675
- * `buildCartesianChart3DLabels`'s category labels, so a path vertex sits
87676
- * directly under its axis label. Z (series/depth axis) uses the exact same
87677
- * formula as `bar-chart-3d-layout.ts`'s clustered box Z, so a line3D/area3D
87678
- * chart's depth planes line up with a bar3D chart's for the same
87679
- * category/series grid.
87680
- *
87681
- * @module cartesian-line-chart-3d-layout
87682
- */
87683
- function normalizeToHeight$1(value, range) {
87684
- if (range.span <= 0) {
87685
- return 0;
87686
- }
87687
- return ((value - range.min) / range.span) * MAX_VALUE_HEIGHT;
87688
- }
87689
- /**
87690
- * Lay out one 3D path per series (grouped from the flat `points` list by
87691
- * `seriesIndex`, sorted by `categoryIndex` within each series). Series with
87692
- * no points are simply absent from the result (there is nothing to path).
87693
- */
87694
- function layoutCartesianLine3DSeries(points, cols, rows, range, depthPercent) {
87695
- const { gridWidth, gridDepth } = computeCartesianGridExtent(cols, rows, depthPercent);
87696
- const rowStep = gridDepth / Math.max(rows, 1);
87697
- const baselineY = normalizeToHeight$1(0, range);
87698
- const bySeries = new Map();
87699
- for (const p of points) {
87700
- const list = bySeries.get(p.seriesIndex);
87701
- if (list) {
87702
- list.push(p);
87703
- }
87704
- else {
87705
- bySeries.set(p.seriesIndex, [p]);
87706
- }
87707
- }
87708
- const paths = [];
87709
- for (const [seriesIndex, seriesPoints] of bySeries) {
87710
- const sorted = [...seriesPoints].sort((a, b) => a.categoryIndex - b.categoryIndex);
87711
- const z = -gridDepth / 2 + rowStep * (seriesIndex + 0.5);
87712
- const vertices = sorted.map((p) => {
87713
- const x = -gridWidth / 2 + (p.categoryIndex / Math.max(cols - 1, 1)) * gridWidth;
87714
- const y = normalizeToHeight$1(p.value, range);
87715
- return {
87716
- seriesIndex,
87717
- categoryIndex: p.categoryIndex,
87718
- value: p.value,
87719
- position: [x, y, z],
87720
- };
87721
- });
87722
- paths.push({
87723
- seriesIndex,
87724
- color: sorted[0]?.color ?? '#4472C4',
87725
- vertices,
87726
- depthZ: z,
87727
- baselineY,
87728
- });
87729
- }
87730
- return paths.sort((a, b) => a.seriesIndex - b.seriesIndex);
87731
- }
87732
- /**
87733
- * Flatten a series path's vertices + baseline into an area3D fill ribbon: for
87734
- * every pair of adjacent category vertices, two triangles connect the top
87735
- * (value) edge to the bottom (baseline) edge. Returns a flat
87736
- * `[x, y, z, x, y, z, ...]` triangle-list ready for a `BufferGeometry`
87737
- * position attribute (9 numbers per triangle, 2 triangles per segment).
87478
+ * The inline-editing layer over a 3D SmartArt view (framework-agnostic).
87738
87479
  *
87739
- * A single-vertex path (one category) has no segment to fill and returns an
87740
- * empty array; line3D never calls this (it only needs the raw vertex path for
87741
- * its tube curve).
87742
- */
87743
- function buildAreaRibbonTriangles(path) {
87744
- const { vertices, baselineY } = path;
87745
- const out = [];
87746
- for (let i = 0; i < vertices.length - 1; i++) {
87747
- const [x1, y1, z1] = vertices[i].position;
87748
- const [x2, y2, z2] = vertices[i + 1].position;
87749
- // Top edge (the authored values) and bottom edge (the baseline), wound so
87750
- // both triangles face the same way (+Y-ish normal) for consistent shading.
87751
- out.push(x1, y1, z1, x1, baselineY, z1, x2, baselineY, z2);
87752
- out.push(x1, y1, z1, x2, baselineY, z2, x2, y2, z2);
87753
- }
87754
- return out;
87755
- }
87756
-
87757
- /** Build the resolved (value + colour) points, row-major (series x category). */
87758
- function buildPoints$1(chartData, catCount) {
87759
- const series = chartData.series;
87760
- const palette = chartData.colorPalette;
87761
- const points = [];
87762
- for (let si = 0; si < series.length; si++) {
87763
- const baseColor = seriesColor(series[si], si, palette);
87764
- for (let ci = 0; ci < catCount; ci++) {
87765
- const value = series[si].values[ci] ?? 0;
87766
- const color = resolveDataPointFill(series[si], ci, paletteColor(si, palette)) ?? baseColor;
87767
- points.push({ seriesIndex: si, categoryIndex: ci, value, color });
87768
- }
87769
- }
87770
- return points;
87771
- }
87772
- /**
87773
- * Build the {@link CartesianLine3DSceneOptions} `createLineChart3DScene`/
87774
- * `createAreaChart3DScene` need, or `null` when the chart has no plottable grid (no
87775
- * series, or every series has zero categories). Chart-type-agnostic: the
87776
- * caller (`buildLineChart3DDataForElement` / `buildAreaChart3DDataForElement`)
87777
- * is the one that gates on the authored `c:chartType`.
87778
- */
87779
- function buildCartesianLine3DSceneData(chartData, categoryLabels, options) {
87780
- const rows = chartData.series.length;
87781
- const cols = categoryLabels.length;
87782
- if (rows === 0 || cols === 0) {
87783
- return null;
87784
- }
87785
- const points = buildPoints$1(chartData, cols);
87786
- // See the matching comment in `bar-chart-3d-data.ts`: the element height
87787
- // stands in for a plot-area height this 3D scene doesn't separately compute.
87788
- const range = computeValueRange(chartData.series, options.height);
87789
- const depthPercent = chartData.view3D?.depthPercent;
87790
- const series = layoutCartesianLine3DSeries(points, cols, rows, range, depthPercent);
87791
- return {
87792
- cols,
87793
- rows,
87794
- series,
87795
- categoryLabels,
87796
- seriesNames: chartData.series.map((s) => s.name),
87797
- numberFormats: chartData.series.map((s) => s.numberFormat),
87798
- width: options.width,
87799
- height: options.height,
87800
- view3D: chartData.view3D
87801
- ? {
87802
- rotX: chartData.view3D.rotX,
87803
- rotY: chartData.view3D.rotY,
87804
- rperspective: chartData.view3D.perspective,
87805
- depthPercent: chartData.view3D.depthPercent,
87806
- rAngAx: chartData.view3D.rAngAx,
87807
- }
87808
- : undefined,
87809
- wallColors: resolveChart3DWallColors(chartData),
87810
- };
87811
- }
87812
-
87813
- /**
87814
- * Single decision point every binding calls to decide whether a chart element
87815
- * should mount the interactive 3D area scene. Mirrors
87816
- * {@link ./line-chart-3d-data.ts}'s `buildLineChart3DDataForElement} exactly,
87817
- * gated on `c:chartType === 'area3D'` instead.
87480
+ * `<pptx-three-view>` keeps a SmartArt's 2D SVG only as its fallback, hidden
87481
+ * once the scene is up, so a binding that edits node text on the canvas lays
87482
+ * a second render of the diagram over the scene with its SVG paint hidden:
87483
+ * its `[data-smartart-node-id]` groups still take the double-click and the
87484
+ * textarea it opens is not SVG, so it shows. That layer is an input surface,
87485
+ * not a second copy of the element, so its element markers go.
87818
87486
  *
87819
- * Returns `null` when the element is not a chart, its `c:chartType` is not
87820
- * literally `area3D`, or the chart has no plottable grid. A non-null result
87821
- * means "render the WebGL scene"; `null` means "fall back to the flat SVG
87822
- * oblique-projection area3D renderer".
87487
+ * @module smartart-3d-edit-layer
87823
87488
  */
87824
- function buildAreaChart3DDataForElement(element, options) {
87825
- if (element.type !== 'chart') {
87826
- return null;
87827
- }
87828
- const chartEl = element;
87829
- const chartData = chartEl.chartData;
87830
- if (!chartData || chartData.series.length === 0) {
87831
- return null;
87832
- }
87833
- if (chartData.chartType !== 'area3D') {
87834
- return null;
87835
- }
87836
- const longestLen = chartData.series.reduce((m, s) => Math.max(m, s.values.length), 0);
87837
- const categoryLabels = chartData.categories.length > 0
87838
- ? chartData.categories
87839
- : Array.from({ length: longestLen }, (_, i) => String(i + 1));
87840
- return buildCartesianLine3DSceneData(chartData, categoryLabels, options);
87841
- }
87842
-
87843
- /** Fraction (0-1) of the base radius `coneToMax`/`pyramidToMax` tapers to at the top. */
87844
- function coneToMaxTopRadiusFactor(shape, value, max) {
87845
- if (shape !== 'coneToMax' && shape !== 'pyramidToMax') {
87846
- return undefined;
87847
- }
87848
- if (!(max > 0)) {
87849
- return 0;
87850
- }
87851
- return Math.max(0, Math.min(1, 1 - Math.abs(value) / max));
87852
- }
87853
- const MIN_BOX_HEIGHT = 0.02;
87854
- const BOX_GAP = 0.15;
87855
- function normalizeToHeight(value, range) {
87856
- if (range.span <= 0) {
87857
- return 0;
87858
- }
87859
- return ((value - range.min) / range.span) * MAX_VALUE_HEIGHT;
87860
- }
87861
- /** Clustered box layout: each series gets its own depth ("Z") plane. */
87862
- function layoutClustered(points, cols, rows, range, depthPercent) {
87863
- const { gridWidth, gridDepth } = computeCartesianGridExtent(cols, rows, depthPercent);
87864
- const colStep = gridWidth / cols;
87865
- const rowStep = gridDepth / Math.max(rows, 1);
87866
- const boxW = colStep * (1 - BOX_GAP);
87867
- const boxD = rowStep * (1 - BOX_GAP);
87868
- const zeroH = normalizeToHeight(0, range);
87869
- return points.map((p) => {
87870
- const valueH = normalizeToHeight(p.plotValue, range);
87871
- const top = Math.max(valueH, zeroH);
87872
- const bottom = Math.min(valueH, zeroH);
87873
- const h = Math.max(top - bottom, MIN_BOX_HEIGHT);
87874
- const x = -gridWidth / 2 + colStep * (p.categoryIndex + 0.5);
87875
- const z = -gridDepth / 2 + rowStep * (p.seriesIndex + 0.5);
87876
- return {
87877
- seriesIndex: p.seriesIndex,
87878
- categoryIndex: p.categoryIndex,
87879
- value: p.value,
87880
- color: p.color,
87881
- center: [x, bottom + h / 2, z],
87882
- size: [boxW, h, boxD],
87883
- shape: p.shape,
87884
- coneToMaxTopRadiusFactor: coneToMaxTopRadiusFactor(p.shape, p.value, range.max),
87885
- };
87886
- });
87887
- }
87888
- /** Stacked/percentStacked box layout: one coplanar depth plane, vertical stack. */
87889
- function layoutStacked(points, cols, rows, range, depthPercent) {
87890
- const { gridWidth, gridDepth } = computeCartesianGridExtent(cols, rows, depthPercent);
87891
- const colStep = gridWidth / cols;
87892
- const boxW = colStep * (1 - BOX_GAP);
87893
- const boxD = gridDepth * (1 - BOX_GAP);
87894
- const boxes = [];
87895
- for (let ci = 0; ci < cols; ci++) {
87896
- const x = -gridWidth / 2 + colStep * (ci + 0.5);
87897
- let posRunning = 0;
87898
- let negRunning = 0;
87899
- const catPoints = points
87900
- .filter((p) => p.categoryIndex === ci)
87901
- .sort((a, b) => a.seriesIndex - b.seriesIndex);
87902
- for (const p of catPoints) {
87903
- const isNeg = p.plotValue < 0;
87904
- const base = isNeg ? negRunning : posRunning;
87905
- const top = base + p.plotValue;
87906
- if (isNeg) {
87907
- negRunning = top;
87489
+ /** Element markers a second render of a diagram must not repeat. */
87490
+ const EDIT_LAYER_MARKER_ATTRS = [
87491
+ 'data-element-id',
87492
+ 'data-testid',
87493
+ 'data-pptx-element',
87494
+ 'role',
87495
+ 'aria-label',
87496
+ 'aria-roledescription',
87497
+ ];
87498
+ /** Remove the element markers from `root` and everything under it. */
87499
+ function stripEditLayerMarkers(root) {
87500
+ for (const el of [root, ...root.querySelectorAll('*')]) {
87501
+ for (const attr of EDIT_LAYER_MARKER_ATTRS) {
87502
+ if (el.hasAttribute(attr)) {
87503
+ el.removeAttribute(attr);
87908
87504
  }
87909
- else {
87910
- posRunning = top;
87911
- }
87912
- const baseH = normalizeToHeight(base, range);
87913
- const topH = normalizeToHeight(top, range);
87914
- const h = Math.max(Math.abs(topH - baseH), MIN_BOX_HEIGHT);
87915
- boxes.push({
87916
- seriesIndex: p.seriesIndex,
87917
- categoryIndex: p.categoryIndex,
87918
- value: p.value,
87919
- color: p.color,
87920
- center: [x, Math.min(baseH, topH) + h / 2, 0],
87921
- size: [boxW, h, boxD],
87922
- shape: p.shape,
87923
- coneToMaxTopRadiusFactor: coneToMaxTopRadiusFactor(p.shape, p.value, range.max),
87924
- });
87925
87505
  }
87926
87506
  }
87927
- return boxes;
87928
87507
  }
87929
87508
  /**
87930
- * Build the box-mesh layout for a resolved grouping: clustered gives every
87931
- * series its own depth plane; stacked/percentStacked keeps them coplanar and
87932
- * stacks vertically (percentStacked's `plotValue`s are pre-normalised to
87933
- * percent by the caller, so this function treats it identically to `stacked`).
87934
- *
87935
- * `horizontal` (`c:barDir val="bar"`) remaps every box's CENTER from the
87936
- * vertical local frame (category = X, value = Y, series = Z) into the
87937
- * horizontal one (value = X, category = Y, series unchanged on Z) via
87938
- * {@link transposeForHorizontalBar3D}. `size` is deliberately left in the
87939
- * unrotated local frame: the scene mounts every box mesh with a matching
87940
- * `-Math.PI / 2` Z rotation instead of swapping width/height numerically, so
87941
- * a cylinder/cone/pyramid box keeps its true (non-elliptical) cross-section
87942
- * when it ends up lying on its side (see `bar-chart-3d-geometry.ts`).
87509
+ * The `[data-smartart-node-id]` element under viewport point `(x, y)` inside
87510
+ * `root`, by geometry: the smallest node box containing the point. Unlike
87511
+ * `document.elementsFromPoint`, this finds nodes that hit-testing skips, such
87512
+ * as a hidden layer's `pointer-events: none` groups.
87943
87513
  */
87944
- function layoutBarChart3D(points, cols, rows, range, grouping, depthPercent, horizontal = false) {
87945
- const boxes = grouping === 'clustered'
87946
- ? layoutClustered(points, cols, rows, range, depthPercent)
87947
- : layoutStacked(points, cols, rows, range, depthPercent);
87948
- if (!horizontal) {
87949
- return boxes;
87950
- }
87951
- return boxes.map((box) => ({ ...box, center: transposeForHorizontalBar3D(box.center) }));
87952
- }
87953
-
87954
- /** Per-category sum of absolute values, for percentStacked normalisation. */
87955
- function categoryTotals(series, catCount) {
87956
- return Array.from({ length: catCount }, (_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0));
87957
- }
87958
- /** Build the resolved (value + colour) grid points, row-major (series x category). */
87959
- function buildPoints(chartData, catCount, isPercent) {
87960
- const series = chartData.series;
87961
- const palette = chartData.colorPalette;
87962
- const totals = isPercent ? categoryTotals(series, catCount) : undefined;
87963
- const points = [];
87964
- for (let si = 0; si < series.length; si++) {
87965
- const baseColor = seriesColor(series[si], si, palette);
87966
- for (let ci = 0; ci < catCount; ci++) {
87967
- const value = series[si].values[ci] ?? 0;
87968
- const color = resolveDataPointFill(series[si], ci, paletteColor(si, palette)) ?? baseColor;
87969
- const catTotal = totals?.[ci];
87970
- const plotValue = isPercent && catTotal !== undefined && catTotal > 0 ? (value / catTotal) * 100 : value;
87971
- points.push({
87972
- seriesIndex: si,
87973
- categoryIndex: ci,
87974
- value,
87975
- plotValue,
87976
- color,
87977
- // Per-series `c:ser/c:shape` wins over the chart-level `c:bar3DChart/c:shape`.
87978
- shape: series[si].shape ?? chartData.barShape,
87979
- });
87514
+ function smartArtNodeAtPoint(root, x, y) {
87515
+ let best = null;
87516
+ let bestArea = Number.POSITIVE_INFINITY;
87517
+ for (const node of root.querySelectorAll('[data-smartart-node-id]')) {
87518
+ const box = node.getBoundingClientRect();
87519
+ const area = box.width * box.height;
87520
+ if (area > 0 &&
87521
+ x >= box.left &&
87522
+ x <= box.right &&
87523
+ y >= box.top &&
87524
+ y <= box.bottom &&
87525
+ area < bestArea) {
87526
+ best = node;
87527
+ bestArea = area;
87980
87528
  }
87981
87529
  }
87982
- return points;
87983
- }
87984
- /**
87985
- * Build the {@link BarChart3DSceneOptions} `createBarChart3DScene` needs, or `null`
87986
- * when the chart has no plottable grid (no series, or every series has zero
87987
- * categories).
87988
- */
87989
- function buildBarChart3DData(chartData, categoryLabels, options) {
87990
- const rows = chartData.series.length;
87991
- const cols = categoryLabels.length;
87992
- if (rows === 0 || cols === 0) {
87993
- return null;
87994
- }
87995
- const grouping = chartData.grouping === 'stacked' || chartData.grouping === 'percentStacked'
87996
- ? chartData.grouping
87997
- : 'clustered';
87998
- const isPercent = grouping === 'percentStacked';
87999
- const horizontal = chartData.barDirection === 'bar';
88000
- const points = buildPoints(chartData, cols, isPercent);
88001
- // No separate plot-area inset exists for the 3D scene the way the flat SVG
88002
- // engine's `computePlotLayout` reserves one, so the full element height
88003
- // stands in for it; see `axisTargetIntervals`'s doc comment for why this
88004
- // only makes the automatic scale saturate at its gridline cap slightly
88005
- // sooner; it does not change the "ordinary chart" result this was fixed for.
88006
- const range = isPercent
88007
- ? { min: 0, max: 100, span: 100 }
88008
- : grouping === 'stacked'
88009
- ? computeStackedValueRange(chartData.series, cols, options.height)
88010
- : computeValueRange(chartData.series, options.height);
88011
- const depthPercent = chartData.view3D?.depthPercent;
88012
- const boxes = layoutBarChart3D(points, cols, rows, range, grouping, depthPercent, horizontal);
88013
- return {
88014
- cols,
88015
- rows,
88016
- boxes,
88017
- categoryLabels,
88018
- seriesNames: chartData.series.map((s) => s.name),
88019
- numberFormats: chartData.series.map((s) => s.numberFormat),
88020
- grouping,
88021
- width: options.width,
88022
- height: options.height,
88023
- view3D: chartData.view3D
88024
- ? {
88025
- rotX: chartData.view3D.rotX,
88026
- rotY: chartData.view3D.rotY,
88027
- rperspective: chartData.view3D.perspective,
88028
- depthPercent: chartData.view3D.depthPercent,
88029
- rAngAx: chartData.view3D.rAngAx,
88030
- }
88031
- : undefined,
88032
- wallColors: resolveChart3DWallColors(chartData),
88033
- picture: { series: chartData.series },
88034
- ...(horizontal ? { horizontal: true } : {}),
88035
- };
88036
- }
88037
- /**
88038
- * Single decision point every binding calls to decide whether a chart element
88039
- * should mount the interactive 3D bar scene: resolves the category-label
88040
- * fallback (mirrors `buildChartViewModel`'s derivation exactly, so 2D and 3D
88041
- * never disagree about what a category is called) and the box layout, in one
88042
- * place.
88043
- *
88044
- * Returns `null` when the element is not a chart, its `c:chartType` is not
88045
- * literally `bar3D` (a plain `bar` chart never mounts the 3D scene, even
88046
- * though `resolveChartKind` folds both onto the same 'bar' kind), or the
88047
- * chart has no plottable grid. A non-null result means "render the WebGL
88048
- * scene"; `null` means "fall back to the flat SVG oblique-projection bar3D
88049
- * renderer". A horizontal 3-D Bar (`c:barDir="bar"`) mounts the SAME scene
88050
- * with its boxes remapped into the horizontal frame (`BarChart3DSceneOptions
88051
- * .horizontal`); see `layoutBarChart3D`.
88052
- */
88053
- function buildBarChart3DDataForElement(element, options) {
88054
- if (element.type !== 'chart') {
88055
- return null;
88056
- }
88057
- const chartEl = element;
88058
- const chartData = chartEl.chartData;
88059
- if (!chartData || chartData.series.length === 0) {
88060
- return null;
88061
- }
88062
- if (chartData.chartType !== 'bar3D') {
88063
- return null;
88064
- }
88065
- const longestLen = chartData.series.reduce((m, s) => Math.max(m, s.values.length), 0);
88066
- const categoryLabels = chartData.categories.length > 0
88067
- ? chartData.categories
88068
- : Array.from({ length: longestLen }, (_, i) => String(i + 1));
88069
- return buildBarChart3DData(chartData, categoryLabels, options);
87530
+ return best;
88070
87531
  }
88071
87532
 
88072
87533
  /** Value-axis label text (`percentStacked` axes read as whole percents). */
@@ -89067,273 +88528,6 @@ function resolveChart3DProjection(chartType, view3D) {
89067
88528
  };
89068
88529
  }
89069
88530
 
89070
- /**
89071
- * Single decision point every binding calls to decide whether a chart element
89072
- * should mount the interactive 3D line scene: resolves the category-label
89073
- * fallback (mirrors `buildChartViewModel`'s derivation exactly, so 2D and 3D
89074
- * never disagree about what a category is called) and the per-series path
89075
- * layout, in one place.
89076
- *
89077
- * Returns `null` when the element is not a chart, its `c:chartType` is not
89078
- * literally `line3D` (a plain `line` chart never mounts the 3D scene), or the
89079
- * chart has no plottable grid. A non-null result means "render the WebGL
89080
- * scene"; `null` means "fall back to the flat SVG oblique-projection line3D
89081
- * renderer".
89082
- */
89083
- function buildLineChart3DDataForElement(element, options) {
89084
- if (element.type !== 'chart') {
89085
- return null;
89086
- }
89087
- const chartEl = element;
89088
- const chartData = chartEl.chartData;
89089
- if (!chartData || chartData.series.length === 0) {
89090
- return null;
89091
- }
89092
- if (chartData.chartType !== 'line3D') {
89093
- return null;
89094
- }
89095
- const longestLen = chartData.series.reduce((m, s) => Math.max(m, s.values.length), 0);
89096
- const categoryLabels = chartData.categories.length > 0
89097
- ? chartData.categories
89098
- : Array.from({ length: longestLen }, (_, i) => String(i + 1));
89099
- return buildCartesianLine3DSceneData(chartData, categoryLabels, options);
89100
- }
89101
-
89102
- /** World-space outer radius of the pie disc. Matches the bar3D scene's world scale. */
89103
- const PIE_RADIUS = 1;
89104
- /** Wedge thickness (world units) at the ECMA-376 default `hPercent` of 100. */
89105
- const BASE_THICKNESS_RATIO = 0.3;
89106
- /** Never let an extreme `hPercent` collapse the wedges to an invisible sliver, or blow up past a chunky block. */
89107
- const MIN_THICKNESS_SCALE = 0.05;
89108
- const MAX_THICKNESS_SCALE = 3;
89109
- /** Margin so the camera frames the disc with room for exploded slices + labels. */
89110
- const CAMERA_EXTENT_MARGIN = 1.35;
89111
- /**
89112
- * Resolve the wedge thickness (world units, the Y-axis extent of every
89113
- * `CylinderGeometry` wedge) from the authored `c:view3D/@hPercent`. Absent
89114
- * uses the ECMA-376 default of 100 (percent), matching
89115
- * {@link ./cartesian-chart-3d-geom.ts}'s `depthPercent` clamp shape.
89116
- */
89117
- function computePieChart3DThickness(view3D) {
89118
- const scale = Math.min(Math.max((view3D?.hPercent ?? 100) / 100, MIN_THICKNESS_SCALE), MAX_THICKNESS_SCALE);
89119
- return PIE_RADIUS * BASE_THICKNESS_RATIO * scale;
89120
- }
89121
- /**
89122
- * Camera placement that frames the pie disc (plus its wedge thickness),
89123
- * reusing {@link computeSphericalCameraPlacement} - the same
89124
- * elevation/azimuth/FOV approach `bar3D` uses - with a pie-appropriate extent
89125
- * (radius, not a category/series grid) and a look-at height centred on the
89126
- * puck's thickness.
89127
- */
89128
- function computePieChart3DCameraPlacement(view3D) {
89129
- const thickness = computePieChart3DThickness(view3D);
89130
- const maxExtent = PIE_RADIUS * CAMERA_EXTENT_MARGIN;
89131
- return computeSphericalCameraPlacement(maxExtent, thickness / 2, view3D);
89132
- }
89133
- /**
89134
- * Build the per-slice angle + explosion geometry for a pie3D scene, mirroring
89135
- * `computePieSlices`' cumulative-angle bookkeeping (`chart-view-model.ts`) so
89136
- * the true-3D wedges and the flat SVG fallback always agree on where each
89137
- * slice starts, how wide it sweeps, and how far `c:explosion` pulls it out.
89138
- *
89139
- * `firstSliceAngleDeg` is `c:firstSliceAng` (degrees clockwise from 12
89140
- * o'clock); `explosions` is the per-point pull-out distance as a percentage
89141
- * of the outer radius (0-100), aligned index-for-index with `values`.
89142
- */
89143
- function computePieChart3DSliceAngles(values, explosions, firstSliceAngleDeg, outerRadius) {
89144
- const total = values.reduce((s, v) => s + Math.abs(v), 0) || 1;
89145
- let cumAngle = -Math.PI / 2 + ((firstSliceAngleDeg ?? 0) * Math.PI) / 180;
89146
- return values.map((value, pointIndex) => {
89147
- const sliceAngle = (Math.abs(value) / total) * Math.PI * 2;
89148
- const startAngle = cumAngle;
89149
- cumAngle += sliceAngle;
89150
- const explosion = explosions?.[pointIndex] ?? 0;
89151
- let explodeOffset = [0, 0];
89152
- if (explosion > 0) {
89153
- const mid = (startAngle + cumAngle) / 2;
89154
- const offset = outerRadius * (explosion / 100);
89155
- explodeOffset = [Math.cos(mid) * offset, Math.sin(mid) * offset];
89156
- }
89157
- return { pointIndex, value, startAngle, thetaLength: sliceAngle, explodeOffset };
89158
- });
89159
- }
89160
-
89161
- /**
89162
- * Build the {@link PieChart3DSceneOptions} `createPieChart3DScene` needs, or `null`
89163
- * when the chart has no plottable series (no series, or the (only) series has
89164
- * no values).
89165
- */
89166
- function buildPieChart3DData(chartData, categoryLabels, options) {
89167
- const pieSeries = chartData.series[0];
89168
- if (!pieSeries || pieSeries.values.length === 0) {
89169
- return null;
89170
- }
89171
- const values = pieSeries.values;
89172
- const explosions = values.map((_v, i) => resolveDataPointExplosion(pieSeries, i));
89173
- const outerRadius = PIE_RADIUS;
89174
- const angles = computePieChart3DSliceAngles(values, explosions, chartData.firstSliceAngle, outerRadius);
89175
- const wedges = angles.map((angle) => ({
89176
- ...angle,
89177
- color: resolveVaryColorFill(pieSeries, angle.pointIndex, paletteColor(angle.pointIndex, chartData.colorPalette)),
89178
- }));
89179
- const view3D = chartData.view3D
89180
- ? {
89181
- rotX: chartData.view3D.rotX,
89182
- rotY: chartData.view3D.rotY,
89183
- rperspective: chartData.view3D.perspective,
89184
- rAngAx: chartData.view3D.rAngAx,
89185
- hPercent: chartData.view3D.hPercent,
89186
- }
89187
- : undefined;
89188
- return {
89189
- wedges,
89190
- categoryLabels,
89191
- seriesName: pieSeries.name,
89192
- numberFormat: pieSeries.numberFormat,
89193
- outerRadius,
89194
- thickness: computePieChart3DThickness(view3D),
89195
- width: options.width,
89196
- height: options.height,
89197
- view3D,
89198
- values,
89199
- explosions,
89200
- firstSliceAngleDeg: chartData.firstSliceAngle,
89201
- };
89202
- }
89203
- /**
89204
- * Single decision point every binding calls to decide whether a chart element
89205
- * should mount the interactive 3D pie scene: resolves the category-label
89206
- * fallback (mirrors `buildChartViewModel`'s derivation exactly, so 2D and 3D
89207
- * never disagree about what a category is called) and the wedge layout, in
89208
- * one place.
89209
- *
89210
- * Returns `null` when the element is not a chart, its `c:chartType` is not
89211
- * literally `pie3D` (a plain `pie`/`doughnut` chart never mounts the 3D
89212
- * scene, even though `resolveChartKind` folds them onto the same 'pie'
89213
- * kind), or the chart has no plottable series. A non-null result means
89214
- * "render the WebGL scene"; `null` means "fall back to the flat SVG
89215
- * oblique-projection pie3D renderer".
89216
- */
89217
- function buildPieChart3DDataForElement(element, options) {
89218
- if (element.type !== 'chart') {
89219
- return null;
89220
- }
89221
- const chartEl = element;
89222
- const chartData = chartEl.chartData;
89223
- if (!chartData || chartData.series.length === 0) {
89224
- return null;
89225
- }
89226
- if (chartData.chartType !== 'pie3D') {
89227
- return null;
89228
- }
89229
- const pieSeries = chartData.series[0];
89230
- const categoryLabels = chartData.categories.length > 0
89231
- ? chartData.categories
89232
- : Array.from({ length: pieSeries.values.length }, (_, i) => String(i + 1));
89233
- return buildPieChart3DData(chartData, categoryLabels, options);
89234
- }
89235
-
89236
- /**
89237
- * Adapts a chart's `PptxChartData` into the flat typed-array grid the
89238
- * interactive 3D surface scene ({@link ./surface-chart-3d-scene.ts},
89239
- * `createSurfaceChart3DScene`) needs to mount.
89240
- *
89241
- * The SVG surface renderer (`chart-surface-treemap.ts`) and this adapter both
89242
- * normalise values the same way (`computeValueRange` + `surfaceColor`), so the
89243
- * 3D view's colour ramp matches the 2D fallback exactly: one set of chart
89244
- * maths, two presentations.
89245
- *
89246
- * @module surface-chart-3d-data
89247
- */
89248
- /** `c:floor`/`c:sideWall`/`c:backWall` fill colours the 3D scene can paint. */
89249
- function resolveSurfaceColors(chartData) {
89250
- return resolveChart3DWallColors(chartData);
89251
- }
89252
- /**
89253
- * Build the {@link SurfaceChart3DSceneOptions} `createSurfaceChart3DScene` needs from
89254
- * a chart element's data, or `null` when the chart has no plottable grid (no
89255
- * series, or every series has zero categories).
89256
- */
89257
- function buildSurfaceChart3DData(chartData, categoryLabels, options) {
89258
- const seriesCount = chartData.series.length;
89259
- const catCount = categoryLabels.length;
89260
- if (seriesCount === 0 || catCount === 0) {
89261
- return null;
89262
- }
89263
- // See the matching comment in `bar-chart-3d-data.ts`: the element height
89264
- // stands in for a plot-area height this 3D scene doesn't separately compute.
89265
- const range = computeValueRange(chartData.series, options.height);
89266
- const heightMap = new Float32Array(seriesCount * catCount);
89267
- const colorMap = new Float32Array(seriesCount * catCount * 3);
89268
- // Raw (un-normalised) values, kept alongside the [0,1] heightMap so the
89269
- // scene's raycast hover tooltip (`surface-chart-3d-hit-test.ts`) can report
89270
- // the authored value rather than its normalised height.
89271
- const values = new Float32Array(seriesCount * catCount);
89272
- for (let row = 0; row < seriesCount; row++) {
89273
- for (let col = 0; col < catCount; col++) {
89274
- const idx = row * catCount + col;
89275
- const val = chartData.series[row]?.values[col] ?? 0;
89276
- values[idx] = val;
89277
- const t = range.span > 0 ? (val - range.min) / range.span : 0;
89278
- heightMap[idx] = t;
89279
- const bandFill = resolveSurfaceBandFill(t, chartData.bandFmts);
89280
- const { r, g, b } = bandFill ? hexToRgb$3(bandFill) : surfaceColor(t);
89281
- const ci = idx * 3;
89282
- colorMap[ci] = r / 255;
89283
- colorMap[ci + 1] = g / 255;
89284
- colorMap[ci + 2] = b / 255;
89285
- }
89286
- }
89287
- return {
89288
- cols: catCount,
89289
- rows: seriesCount,
89290
- heightMap,
89291
- colorMap,
89292
- values,
89293
- numberFormats: chartData.series.map((s) => s.numberFormat),
89294
- wireframe: options.wireframe ?? chartData.wireframe ?? true,
89295
- categoryLabels,
89296
- seriesNames: chartData.series.map((s) => s.name),
89297
- width: options.width,
89298
- height: options.height,
89299
- view3D: chartData.view3D
89300
- ? { rotX: chartData.view3D.rotX, rotY: chartData.view3D.rotY }
89301
- : undefined,
89302
- surfaceColors: resolveSurfaceColors(chartData),
89303
- };
89304
- }
89305
- /**
89306
- * Single decision point every binding calls to decide whether a chart element
89307
- * should mount the interactive 3D surface scene: resolves the chart kind, the
89308
- * category-label fallback (mirrors `buildChartViewModel`'s `categoryLabels`
89309
- * derivation exactly, so 2D and 3D never disagree about what a category is
89310
- * called), and the 3D grid, in one place.
89311
- *
89312
- * Returns `null` when the element is not a chart, its kind does not resolve to
89313
- * `surface` (covers both `surface` and `surface3D` `c:chartType`s), or the
89314
- * chart has no plottable grid. Callers gate the 3D scene on this alone; a
89315
- * non-null result means "render the WebGL view", `null` means "fall back to
89316
- * the SVG isometric/flat surface renderer".
89317
- */
89318
- function buildSurfaceChart3DDataForElement(element, options) {
89319
- if (element.type !== 'chart') {
89320
- return null;
89321
- }
89322
- const chartEl = element;
89323
- const chartData = chartEl.chartData;
89324
- if (!chartData || chartData.series.length === 0) {
89325
- return null;
89326
- }
89327
- if (resolveChartKind(chartData.chartType ?? 'bar') !== 'surface') {
89328
- return null;
89329
- }
89330
- const longestLen = chartData.series.reduce((m, s) => Math.max(m, s.values.length), 0);
89331
- const categoryLabels = chartData.categories.length > 0
89332
- ? chartData.categories
89333
- : Array.from({ length: longestLen }, (_, i) => String(i + 1));
89334
- return buildSurfaceChart3DData(chartData, categoryLabels, options);
89335
- }
89336
-
89337
88531
  /** The chart types the 3D chart scene renders (`surface` covers the 2D and 3D surface charts; see below). */
89338
88532
  const CHART_3D_TYPES = new Set([
89339
88533
  'bar3D',
@@ -89390,6 +88584,10 @@ function buildChart3DSpecForElement(element) {
89390
88584
  : chartType === 'pie3D'
89391
88585
  ? buildPieGeometry(element, vm)
89392
88586
  : null;
88587
+ if (!geometry) {
88588
+ // Nothing to draw in 3D (no series or categories): the 2D chart stays.
88589
+ return null;
88590
+ }
89393
88591
  const longest = chartData.series.reduce((m, series) => Math.max(m, series.values.length), 0);
89394
88592
  const categoryLabels = chartData.categories.length > 0
89395
88593
  ? chartData.categories
@@ -89403,37 +88601,8 @@ function buildChart3DSpecForElement(element) {
89403
88601
  vm,
89404
88602
  geometry,
89405
88603
  categoryLabels,
89406
- perspective: geometry ? null : buildPerspectiveScene(element),
89407
88604
  };
89408
88605
  }
89409
- /** The perspective scene for a chart the oblique geometry does not cover, or `null`. */
89410
- function buildPerspectiveScene(element) {
89411
- const size = { width: element.width, height: element.height };
89412
- switch (element.chartData?.chartType) {
89413
- case 'bar3D': {
89414
- const options = buildBarChart3DDataForElement(element, size);
89415
- return options ? { kind: 'bar', options } : null;
89416
- }
89417
- case 'line3D': {
89418
- const options = buildLineChart3DDataForElement(element, size);
89419
- return options ? { kind: 'line', options } : null;
89420
- }
89421
- case 'area3D': {
89422
- const options = buildAreaChart3DDataForElement(element, size);
89423
- return options ? { kind: 'area', options } : null;
89424
- }
89425
- case 'pie3D': {
89426
- const options = buildPieChart3DDataForElement(element, size);
89427
- return options ? { kind: 'pie', options } : null;
89428
- }
89429
- case 'surface': {
89430
- const options = buildSurfaceChart3DDataForElement(element, size);
89431
- return options ? { kind: 'surface', options } : null;
89432
- }
89433
- default:
89434
- return null;
89435
- }
89436
- }
89437
88606
 
89438
88607
  /**
89439
88608
  * Live height of a DOM element, for layout that has to clear a strip whose
@@ -89789,7 +88958,7 @@ const THREE_UNAVAILABLE = {
89789
88958
  setInteractive: () => { },
89790
88959
  dispose: () => { },
89791
88960
  };
89792
- const FOV$1 = 50;
88961
+ const FOV = 50;
89793
88962
  /** Dynamically load `three`; returns `null` when the package is not installed. */
89794
88963
  async function loadThree() {
89795
88964
  try {
@@ -89868,7 +89037,7 @@ async function mountModel3D(container, modelUrl, options) {
89868
89037
  if (options.background) {
89869
89038
  scene.background = new three.Color(options.background);
89870
89039
  }
89871
- const camera = new three.PerspectiveCamera(FOV$1, width / height, 0.1, 1000);
89040
+ const camera = new three.PerspectiveCamera(FOV, width / height, 0.1, 1000);
89872
89041
  camera.position.set(0, 0, 5);
89873
89042
  camera.lookAt(0, 0, 0);
89874
89043
  scene.add(new three.AmbientLight(0xffffff, 0.5));
@@ -90138,1171 +89307,6 @@ function computeCameraPlacement(cols, rows, view3D) {
90138
89307
  };
90139
89308
  }
90140
89309
 
90141
- /** Pointer events after which a scene may look different (hover, highlight, drag preview). */
90142
- const REDRAW_EVENTS = ['pointermove', 'pointerdown', 'pointerup', 'pointerleave', 'wheel'];
90143
- /** Build the lights, camera and controls every perspective chart scene shares. */
90144
- function createHostedChart3DStage(ctx, placement, limits, ambientIntensity = 0.6) {
90145
- const three = ctx.three;
90146
- const width = Math.max(1, ctx.size.width);
90147
- const height = Math.max(1, ctx.size.height);
90148
- const scene = new three.Scene();
90149
- scene.add(new three.AmbientLight(0xffffff, ambientIntensity));
90150
- const key = new three.DirectionalLight(0xffffff, 0.8);
90151
- key.position.set(5, 8, 5);
90152
- scene.add(key);
90153
- const fill = new three.DirectionalLight(0xffffff, 0.3);
90154
- fill.position.set(-3, 4, -2);
90155
- scene.add(fill);
90156
- const camera = new three.PerspectiveCamera(placement.fov, width / height, 0.1, 1000);
90157
- camera.position.set(...placement.position);
90158
- const target = new three.Vector3(...placement.target);
90159
- camera.lookAt(target);
90160
- let orbit = null;
90161
- if (ctx.OrbitControls) {
90162
- orbit = new ctx.OrbitControls(camera, ctx.eventTarget);
90163
- const limited = orbit;
90164
- limited.enablePan = true;
90165
- limited.enableZoom = true;
90166
- limited.enableRotate = true;
90167
- limited.minDistance = limits.minDistance;
90168
- limited.maxDistance = limits.maxDistance;
90169
- limited.maxPolarAngle = Math.PI / 2 + 0.3;
90170
- limited.target.copy(target);
90171
- limited.enabled = ctx.interactive;
90172
- limited.update();
90173
- }
90174
- return {
90175
- three,
90176
- canvas: ctx.eventTarget,
90177
- scene,
90178
- camera,
90179
- controls: orbit ?? { enabled: false },
90180
- orbit,
90181
- width,
90182
- height,
90183
- };
90184
- }
90185
- /**
90186
- * The value-interaction callbacks a hosted scene hands its pointer wiring:
90187
- * they raise `<pptx-three-view>` select/drag events, and stay silent while the
90188
- * view is not interactive (a read-only mount never touches the selection).
90189
- */
90190
- function hostedChart3DInteraction(ctx) {
90191
- let interactive = ctx.interactive;
90192
- return {
90193
- onSelect: (part) => {
90194
- if (interactive) {
90195
- ctx.emit({ type: 'select', part });
90196
- }
90197
- },
90198
- onValueDragPreview: (part, value) => {
90199
- if (interactive) {
90200
- ctx.emit({ type: 'drag', detail: { part, value, phase: 'move' } });
90201
- }
90202
- },
90203
- onValueDragCommit: (part, value) => {
90204
- if (interactive) {
90205
- ctx.emit({ type: 'drag', detail: { part, value, phase: 'commit' } });
90206
- }
90207
- },
90208
- set: (on) => {
90209
- interactive = on;
90210
- },
90211
- };
90212
- }
90213
- /** Wrap a built stage + its scene-specific parts as the {@link ThreeViewScene} the host drives. */
90214
- function finishHostedChart3DScene(ctx, stage, parts, interactive) {
90215
- let width = stage.width;
90216
- let height = stage.height;
90217
- const requestRender = () => ctx.requestRender();
90218
- stage.orbit?.addEventListener('change', requestRender);
90219
- for (const type of REDRAW_EVENTS) {
90220
- ctx.eventTarget.addEventListener(type, requestRender);
90221
- }
90222
- let disposed = false;
90223
- return {
90224
- render(renderer) {
90225
- stage.orbit?.update();
90226
- renderer.render(stage.scene, stage.camera);
90227
- parts.afterRender?.(stage.camera, width, height);
90228
- },
90229
- resize(size) {
90230
- width = Math.max(1, size.width);
90231
- height = Math.max(1, size.height);
90232
- stage.camera.aspect = width / height;
90233
- stage.camera.updateProjectionMatrix();
90234
- parts.resize?.(width, height);
90235
- },
90236
- isAnimating: () => false,
90237
- setInteractive(on) {
90238
- interactive.set(on);
90239
- if (stage.orbit) {
90240
- stage.orbit.enabled = on;
90241
- }
90242
- },
90243
- setSelectedPart(part) {
90244
- parts.setSelectedPart?.(part);
90245
- },
90246
- setTextStyle(style) {
90247
- parts.setTextStyle?.(style);
90248
- },
90249
- dispose() {
90250
- if (disposed) {
90251
- return;
90252
- }
90253
- disposed = true;
90254
- stage.orbit?.removeEventListener('change', requestRender);
90255
- for (const type of REDRAW_EVENTS) {
90256
- ctx.eventTarget.removeEventListener(type, requestRender);
90257
- }
90258
- stage.orbit?.dispose();
90259
- parts.dispose();
90260
- stage.scene.clear();
90261
- },
90262
- };
90263
- }
90264
-
90265
- /** Attach the shared raycast-hover-tooltip wiring to one scene's canvas. */
90266
- function attachChart3DHoverTooltip(options) {
90267
- const raycaster = new options.three.Raycaster();
90268
- const ndc = new options.three.Vector2();
90269
- let hoveredTooltip;
90270
- const updateHoverTooltip = (clientX, clientY) => {
90271
- const rect = options.canvas.getBoundingClientRect();
90272
- if (rect.width <= 0 || rect.height <= 0) {
90273
- return;
90274
- }
90275
- ndc.x = ((clientX - rect.left) / rect.width) * 2 - 1;
90276
- ndc.y = -((clientY - rect.top) / rect.height) * 2 + 1;
90277
- raycaster.setFromCamera(ndc, options.camera);
90278
- const hit = raycaster.intersectObjects(options.meshes, false)[0];
90279
- const tooltip = options.buildTooltip(hit);
90280
- if (tooltip !== hoveredTooltip) {
90281
- hoveredTooltip = tooltip;
90282
- options.canvas.title = tooltip ?? '';
90283
- }
90284
- };
90285
- const onPointerMove = (event) => {
90286
- updateHoverTooltip(event.clientX, event.clientY);
90287
- };
90288
- const onPointerLeave = () => {
90289
- if (hoveredTooltip !== undefined) {
90290
- hoveredTooltip = undefined;
90291
- options.canvas.title = '';
90292
- }
90293
- };
90294
- options.canvas.addEventListener('pointermove', onPointerMove);
90295
- options.canvas.addEventListener('pointerleave', onPointerLeave);
90296
- return {
90297
- dispose() {
90298
- options.canvas.removeEventListener('pointermove', onPointerMove);
90299
- options.canvas.removeEventListener('pointerleave', onPointerLeave);
90300
- },
90301
- };
90302
- }
90303
-
90304
- /** Base (un-emphasised) font size, in CSS px; `fontScale` multiplies this, matching every other text surface's relative-size convention. */
90305
- const BASE_FONT_SIZE_PX = 9;
90306
- /** Create the DOM overlay nodes for the axis labels, returned with the layer. */
90307
- function createLabelOverlay(doc, labels) {
90308
- const layer = doc.createElement('div');
90309
- Object.assign(layer.style, {
90310
- position: 'absolute',
90311
- inset: '0',
90312
- pointerEvents: 'none',
90313
- overflow: 'hidden',
90314
- });
90315
- const entries = labels.map((label) => {
90316
- const node = doc.createElement('div');
90317
- node.textContent = label.text;
90318
- const baseColor = label.axis === 'value' ? '#999' : '#666';
90319
- Object.assign(node.style, {
90320
- position: 'absolute',
90321
- fontSize: `${BASE_FONT_SIZE_PX}px`,
90322
- color: baseColor,
90323
- whiteSpace: 'nowrap',
90324
- userSelect: 'none',
90325
- transform: 'translate(-50%, -50%)',
90326
- willChange: 'left, top',
90327
- });
90328
- if (label.axis === 'value') {
90329
- node.style.writingMode = 'vertical-rl';
90330
- }
90331
- layer.appendChild(node);
90332
- return { node, baseColor };
90333
- });
90334
- function applyTextStyle(style) {
90335
- for (const { node, baseColor } of entries) {
90336
- node.style.fontWeight = style?.bold === undefined ? '' : style.bold ? 'bold' : 'normal';
90337
- node.style.fontStyle = style?.italic === undefined ? '' : style.italic ? 'italic' : 'normal';
90338
- node.style.textDecorationLine =
90339
- style?.underline === undefined ? '' : style.underline ? 'underline' : 'none';
90340
- const scale = typeof style?.fontScale === 'number' &&
90341
- Number.isFinite(style.fontScale) &&
90342
- style.fontScale > 0
90343
- ? style.fontScale
90344
- : 1;
90345
- node.style.fontSize = `${BASE_FONT_SIZE_PX * scale}px`;
90346
- node.style.color = style?.color ?? baseColor;
90347
- }
90348
- }
90349
- return { layer, nodes: entries.map((e) => e.node), applyTextStyle };
90350
- }
90351
-
90352
- /** Build the label overlay and its per-frame projector for one scene. */
90353
- function createChart3DLabelProjector(three, doc, labels) {
90354
- const overlay = createLabelOverlay(doc, labels);
90355
- const anchors = labels.map((l) => new three.Vector3(...l.anchor));
90356
- const projected = new three.Vector3();
90357
- return {
90358
- layer: overlay.layer,
90359
- applyTextStyle: overlay.applyTextStyle,
90360
- update(camera, width, height) {
90361
- for (let i = 0; i < overlay.nodes.length; i++) {
90362
- projected.copy(anchors[i]).project(camera);
90363
- const node = overlay.nodes[i];
90364
- if (projected.z > 1) {
90365
- node.style.display = 'none';
90366
- continue;
90367
- }
90368
- node.style.display = '';
90369
- node.style.left = `${((projected.x + 1) / 2) * width}px`;
90370
- node.style.top = `${((-projected.y + 1) / 2) * height}px`;
90371
- }
90372
- },
90373
- };
90374
- }
90375
-
90376
- /**
90377
- * Pure raycast-to-data-cell mapping for the interactive 3D surface chart mesh
90378
- * ({@link ./surface-chart-3d-scene.ts}).
90379
- *
90380
- * Every other chart kind's hover tooltip (bar / line / area / scatter / bubble
90381
- * / pie / radar / map) is projected as a native SVG `<title>` child on the
90382
- * mark itself (`buildMarkTooltip` in `chart-view-model.ts`); a browser shows
90383
- * that title on hover for free. The WebGL surface mesh has no SVG marks to
90384
- * attach a `<title>` to, so the scene controller raycasts the pointer against
90385
- * the mesh, maps the hit back to the (series, category) cell it belongs to
90386
- * with the pure functions here, and sets that same `buildMarkTooltip` text as
90387
- * the canvas element's own `title` attribute, i.e. the identical native-tooltip
90388
- * mechanism every other mark already uses.
90389
- *
90390
- * Kept three-agnostic (no runtime `three` import, only a raw face index in and
90391
- * a row/col out) so the row/col math is unit-testable without mocking WebGL.
90392
- *
90393
- * @module surface-chart-3d-hit-test
90394
- */
90395
- /**
90396
- * Map a three.js `Raycaster` intersection's `faceIndex` (a triangle index into
90397
- * the surface mesh, built by `buildSurfaceGeometry`'s `THREE.PlaneGeometry`)
90398
- * back to the (row, col) grid cell whose facet the triangle belongs to.
90399
- *
90400
- * `PlaneGeometry(width, height, widthSegs, depthSegs)` emits `widthSegs *
90401
- * depthSegs` quads in row-major order, each split into 2 triangles, so
90402
- * `floor(faceIndex / 2)` is the quad index and `floor(quadIndex / widthSegs)`
90403
- * / `quadIndex % widthSegs` recover its row/col. A facet spans the four grid
90404
- * vertices `[row, col]..[row+1, col+1]`; it is reported anchored at its
90405
- * top-left corner `(row, col)`, mirroring the SVG isometric surface renderer's
90406
- * `part: { seriesIndex: row, pointIndex: col }` anchor convention
90407
- * (`chart-surface-treemap.ts`) so both presentations agree on which data point
90408
- * a given facet "is".
90409
- *
90410
- * Returns `null` when `faceIndex` is out of range for a `cols x rows` grid
90411
- * (fewer than 2 columns or rows has no facets at all).
90412
- */
90413
- function surfaceFaceIndexToCell(faceIndex, cols, rows) {
90414
- const quadsPerRow = cols - 1;
90415
- const quadsPerCol = rows - 1;
90416
- if (!Number.isFinite(faceIndex) || faceIndex < 0 || quadsPerRow <= 0 || quadsPerCol <= 0) {
90417
- return null;
90418
- }
90419
- const quadIndex = Math.floor(faceIndex / 2);
90420
- const row = Math.floor(quadIndex / quadsPerRow);
90421
- const col = quadIndex % quadsPerRow;
90422
- if (row < 0 || row >= quadsPerCol || col < 0 || col >= quadsPerRow) {
90423
- return null;
90424
- }
90425
- return { row, col };
90426
- }
90427
- /**
90428
- * Build the hover-tooltip text for a raycast hit on the surface mesh, or
90429
- * `undefined` when there is no hit, the hit falls outside the data grid, or
90430
- * the scene was mounted without raw `values` (older callers that only supply
90431
- * the normalised height/colour maps keep rendering with no tooltip).
90432
- *
90433
- * Mirrors `buildMarkTooltip`'s "<series>, <category>: <value>" text exactly,
90434
- * so a surface facet's tooltip reads identically to every other chart kind's.
90435
- */
90436
- function buildSurfaceHoverTooltip(faceIndex, data) {
90437
- if (faceIndex === undefined || faceIndex === null || !data.values) {
90438
- return undefined;
90439
- }
90440
- const cell = surfaceFaceIndexToCell(faceIndex, data.cols, data.rows);
90441
- if (!cell) {
90442
- return undefined;
90443
- }
90444
- const idx = cell.row * data.cols + cell.col;
90445
- const value = data.values[idx];
90446
- if (value === undefined) {
90447
- return undefined;
90448
- }
90449
- return buildMarkTooltip(data.seriesNames[cell.row], data.categoryLabels[cell.col], value, data.numberFormats?.[cell.row]);
90450
- }
90451
-
90452
- /**
90453
- * Minimum pointer travel (px) before a mark press becomes a value drag,
90454
- * mirroring {@link CHART_DRAG_THRESHOLD_PX} (`chart-canvas-drag.ts`): without
90455
- * a threshold, every click on a mark would commit a (tiny) value change, so a
90456
- * user could never select a mark without editing it.
90457
- */
90458
- const CHART_3D_DRAG_THRESHOLD_PX = 3;
90459
- /** Emissive colour + intensity applied to a selected mark's material. */
90460
- const CHART_3D_SELECTED_EMISSIVE = '#3b82f6';
90461
- const CHART_3D_SELECTED_EMISSIVE_INTENSITY = 0.55;
90462
- /**
90463
- * Map a 3D raycast hit (or none) to the same selection descriptor the 2D
90464
- * chart-interaction model uses.
90465
- */
90466
- function chart3DHitToPartRef(hit) {
90467
- if (!hit) {
90468
- return null;
90469
- }
90470
- return { role: 'dataPoint', seriesIndex: hit.seriesIndex, pointIndex: hit.pointIndex };
90471
- }
90472
- /** Whether a mesh's own (series, point) reference is the currently-selected part. */
90473
- function chart3DMarkMatchesPart(mark, part) {
90474
- return (part !== null &&
90475
- part !== undefined &&
90476
- part.role === 'dataPoint' &&
90477
- part.seriesIndex === mark.seriesIndex &&
90478
- part.pointIndex === mark.pointIndex);
90479
- }
90480
- /**
90481
- * Calibrate a value axis from two already-projected screen points known to
90482
- * correspond to two distinct data values on the same mark (e.g. its
90483
- * value-zero base and its current top). Returns `null` when the two values
90484
- * coincide or the two screen points are (near) coincident (camera looking
90485
- * straight down the axis), since no calibration is possible.
90486
- *
90487
- * Order-independent: swapping `(screenAtValue0, value0)` with
90488
- * `(screenAtValue1, value1)` yields the same {@link chart3DPointerDeltaToValueDelta}
90489
- * result, since the direction vector and `pixelsPerUnit` sign flip together.
90490
- */
90491
- function calibrateChart3DValueAxis(screenAtValue0, value0, screenAtValue1, value1) {
90492
- const valueSpan = value1 - value0;
90493
- const dx = screenAtValue1.x - screenAtValue0.x;
90494
- const dy = screenAtValue1.y - screenAtValue0.y;
90495
- const pixelSpan = Math.hypot(dx, dy);
90496
- if (valueSpan === 0 || pixelSpan < 1e-6 || !Number.isFinite(valueSpan)) {
90497
- return null;
90498
- }
90499
- return {
90500
- directionScreen: { x: dx / pixelSpan, y: dy / pixelSpan },
90501
- pixelsPerUnit: pixelSpan / valueSpan,
90502
- };
90503
- }
90504
- /**
90505
- * Convert a screen-space pointer delta (current position minus drag-start
90506
- * position, in CSS px) into a value delta, using a calibration taken at drag
90507
- * start. Add the result to the mark's value at drag start to get the live
90508
- * (or committed) dragged value.
90509
- */
90510
- function chart3DPointerDeltaToValueDelta(calibration, pointerDeltaScreen) {
90511
- const dot = pointerDeltaScreen.x * calibration.directionScreen.x +
90512
- pointerDeltaScreen.y * calibration.directionScreen.y;
90513
- return dot / calibration.pixelsPerUnit;
90514
- }
90515
-
90516
- function projectToScreen(three, camera, point, width, height) {
90517
- const v = new three.Vector3(point[0], point[1], point[2]);
90518
- v.project(camera);
90519
- return { x: ((v.x + 1) / 2) * width, y: ((-v.y + 1) / 2) * height };
90520
- }
90521
- /** Attach the shared click-to-select / drag-to-value pointer handlers to one scene's canvas. */
90522
- function attachChart3DPointerInteraction(options) {
90523
- let width = options.width;
90524
- let height = options.height;
90525
- const raycaster = new options.three.Raycaster();
90526
- const ndc = new options.three.Vector2();
90527
- let dragState = null;
90528
- function raycastAt(clientX, clientY) {
90529
- const rect = options.canvas.getBoundingClientRect();
90530
- if (rect.width <= 0 || rect.height <= 0) {
90531
- return undefined;
90532
- }
90533
- ndc.x = ((clientX - rect.left) / rect.width) * 2 - 1;
90534
- ndc.y = -((clientY - rect.top) / rect.height) * 2 + 1;
90535
- raycaster.setFromCamera(ndc, options.camera);
90536
- return raycaster.intersectObjects(options.meshes, false)[0];
90537
- }
90538
- function setSelectedPart(part) {
90539
- options.applyHighlight?.(part);
90540
- }
90541
- function currentDelta(clientX, clientY, state) {
90542
- return { x: clientX - state.startClientX, y: clientY - state.startClientY };
90543
- }
90544
- const onPointerDown = (event) => {
90545
- // Pointer capture keeps delivering move/up to THIS canvas even once the
90546
- // pointer travels outside its bounds mid-drag; optional-chained since
90547
- // test doubles for `canvas` do not implement it.
90548
- options.canvas.setPointerCapture?.(event.pointerId);
90549
- const hit = raycastAt(event.clientX, event.clientY);
90550
- const part = hit ? options.resolveHit(hit) : null;
90551
- const armed = part !== null && isChartInteractionArmed(options.canvas);
90552
- if (armed) {
90553
- // The scene owns this press (select / value-drag): keep it from the
90554
- // stage's element-move handler. See the module doc.
90555
- event.stopPropagation();
90556
- }
90557
- let calibration = null;
90558
- let startValue = 0;
90559
- if (hit && part && armed && options.calibrateDrag) {
90560
- const input = options.calibrateDrag(hit, part);
90561
- if (input) {
90562
- const s0 = projectToScreen(options.three, options.camera, input.worldAtValue0, width, height);
90563
- const s1 = projectToScreen(options.three, options.camera, input.worldAtValue1, width, height);
90564
- calibration = calibrateChart3DValueAxis(s0, input.value0, s1, input.value1);
90565
- startValue = input.value1;
90566
- }
90567
- }
90568
- dragState = {
90569
- part,
90570
- calibration,
90571
- startValue,
90572
- startClientX: event.clientX,
90573
- startClientY: event.clientY,
90574
- moved: false,
90575
- };
90576
- if (calibration) {
90577
- // Cancelling the pointerdown also suppresses its compatibility
90578
- // mousedown, which is what the React stage's DESKTOP element-move
90579
- // path listens to (stopPropagation alone leaves that one alive).
90580
- // Same as every 2D mark press once its drag has started.
90581
- event.preventDefault();
90582
- options.setControlsEnabled?.(false);
90583
- }
90584
- };
90585
- const onPointerMove = (event) => {
90586
- if (!dragState || !dragState.part || !dragState.calibration) {
90587
- return;
90588
- }
90589
- const delta = currentDelta(event.clientX, event.clientY, dragState);
90590
- if (!dragState.moved && Math.hypot(delta.x, delta.y) < CHART_3D_DRAG_THRESHOLD_PX) {
90591
- return;
90592
- }
90593
- dragState.moved = true;
90594
- const value = dragState.startValue + chart3DPointerDeltaToValueDelta(dragState.calibration, delta);
90595
- options.onValueDragPreview?.(dragState.part, value);
90596
- };
90597
- const onPointerUp = (event) => {
90598
- if (!dragState) {
90599
- return;
90600
- }
90601
- const state = dragState;
90602
- dragState = null;
90603
- options.canvas.releasePointerCapture?.(event.pointerId);
90604
- if (state.calibration) {
90605
- options.setControlsEnabled?.(true);
90606
- }
90607
- if (state.moved && state.part && state.calibration) {
90608
- const delta = currentDelta(event.clientX, event.clientY, state);
90609
- const value = state.startValue + chart3DPointerDeltaToValueDelta(state.calibration, delta);
90610
- options.onValueDragCommit?.(state.part, value);
90611
- return;
90612
- }
90613
- setSelectedPart(state.part);
90614
- options.onSelect?.(state.part);
90615
- };
90616
- options.canvas.addEventListener('pointerdown', onPointerDown);
90617
- options.canvas.addEventListener('pointermove', onPointerMove);
90618
- options.canvas.addEventListener('pointerup', onPointerUp);
90619
- options.canvas.addEventListener('pointercancel', onPointerUp);
90620
- return {
90621
- setSelectedPart,
90622
- updateSize(w, h) {
90623
- width = w;
90624
- height = h;
90625
- },
90626
- dispose() {
90627
- options.canvas.removeEventListener('pointerdown', onPointerDown);
90628
- options.canvas.removeEventListener('pointermove', onPointerMove);
90629
- options.canvas.removeEventListener('pointerup', onPointerUp);
90630
- options.canvas.removeEventListener('pointercancel', onPointerUp);
90631
- },
90632
- };
90633
- }
90634
-
90635
- /**
90636
- * Calibrate a value drag for one surface3D vertex, given its own (already
90637
- * height-displaced) world position, its raw value, and every raw value in the
90638
- * grid (to recover the `[min, max]` the heightMap normalised against; the
90639
- * scene already holds this flat array for the hover tooltip, so no extra data
90640
- * needs threading through). Returns `null` when every cell shares the same
90641
- * value: there is then no vertical axis to calibrate a drag against.
90642
- */
90643
- function calibrateSurfaceChart3DDrag(position, value, values) {
90644
- let min = Infinity;
90645
- let max = -Infinity;
90646
- for (const v of values) {
90647
- if (v < min) {
90648
- min = v;
90649
- }
90650
- if (v > max) {
90651
- max = v;
90652
- }
90653
- }
90654
- if (!(max > min)) {
90655
- return null;
90656
- }
90657
- // A reference point at a DIFFERENT value on the SAME vertical column
90658
- // (fixed x/z), exact by construction: world Y 0 <-> the grid minimum,
90659
- // world Y MAX_HEIGHT <-> the grid maximum. Use whichever end differs from
90660
- // this vertex's own value (a vertex already at the minimum needs the
90661
- // maximum as its reference, and vice versa).
90662
- const [x, , z] = position;
90663
- const atMin = value === min;
90664
- return {
90665
- worldAtValue0: [x, atMin ? MAX_HEIGHT : 0, z],
90666
- value0: atMin ? max : min,
90667
- worldAtValue1: position,
90668
- value1: value,
90669
- };
90670
- }
90671
-
90672
- const MARKER_RADIUS$2 = 0.07;
90673
- /** Lifts the marker just clear of the mesh surface so it never z-fights it. */
90674
- const MARKER_Y_OFFSET = 0.03;
90675
- /**
90676
- * Build the selection marker mesh for a surface3D scene. The caller adds
90677
- * `.mesh` to its scene and disposes the returned handle alongside its other
90678
- * geometries/materials.
90679
- */
90680
- function createSurfaceHighlightMarker(three, cols, rows, heightMap) {
90681
- const geometry = new three.SphereGeometry(MARKER_RADIUS$2, 12, 8);
90682
- const material = new three.MeshPhongMaterial({
90683
- color: CHART_3D_SELECTED_EMISSIVE,
90684
- emissive: CHART_3D_SELECTED_EMISSIVE,
90685
- emissiveIntensity: CHART_3D_SELECTED_EMISSIVE_INTENSITY,
90686
- shininess: 30,
90687
- });
90688
- const mesh = new three.Mesh(geometry, material);
90689
- mesh.visible = false;
90690
- return {
90691
- mesh,
90692
- setSelected(cell) {
90693
- if (!cell) {
90694
- mesh.visible = false;
90695
- return;
90696
- }
90697
- const [x, y, z] = surfaceVertexWorldPosition(cols, rows, cell.row, cell.col, heightMap);
90698
- mesh.position.set(x, y + MARKER_Y_OFFSET, z);
90699
- mesh.visible = true;
90700
- },
90701
- dispose() {
90702
- geometry.dispose();
90703
- material.dispose();
90704
- },
90705
- };
90706
- }
90707
- function cellFromIntersection(intersection, cols, rows) {
90708
- return intersection.faceIndex === undefined || intersection.faceIndex === null
90709
- ? null
90710
- : surfaceFaceIndexToCell(intersection.faceIndex, cols, rows);
90711
- }
90712
- /** Attach click-to-select / drag-to-value handling to a mounted surface3D scene. */
90713
- function attachSurfaceChart3DInteraction(params) {
90714
- return attachChart3DPointerInteraction({
90715
- three: params.three,
90716
- canvas: params.canvas,
90717
- camera: params.camera,
90718
- width: params.width,
90719
- height: params.height,
90720
- meshes: [params.surfaceMesh],
90721
- resolveHit: (intersection) => {
90722
- const cell = cellFromIntersection(intersection, params.cols, params.rows);
90723
- return chart3DHitToPartRef(cell ? { seriesIndex: cell.row, pointIndex: cell.col } : null);
90724
- },
90725
- calibrateDrag: (intersection) => {
90726
- const cell = cellFromIntersection(intersection, params.cols, params.rows);
90727
- if (!cell || !params.values) {
90728
- return null;
90729
- }
90730
- const idx = cell.row * params.cols + cell.col;
90731
- const value = params.values[idx];
90732
- if (value === undefined) {
90733
- return null;
90734
- }
90735
- const position = surfaceVertexWorldPosition(params.cols, params.rows, cell.row, cell.col, params.heightMap);
90736
- return calibrateSurfaceChart3DDrag(position, value, params.values);
90737
- },
90738
- applyHighlight: (part) => {
90739
- const cell = part && part.role === 'dataPoint' && part.pointIndex !== undefined
90740
- ? { row: part.seriesIndex, col: part.pointIndex }
90741
- : null;
90742
- params.highlightMarker.setSelected(cell);
90743
- },
90744
- setControlsEnabled: (enabled) => {
90745
- params.controls.enabled = enabled;
90746
- },
90747
- onSelect: params.interaction?.onSelect,
90748
- onValueDragPreview: params.interaction?.onValueDragPreview,
90749
- onValueDragCommit: params.interaction?.onValueDragCommit,
90750
- });
90751
- }
90752
-
90753
- const PANEL_OPACITY = 0.35;
90754
- /**
90755
- * Build plane meshes for each authored surface colour. The floor lies flat
90756
- * under the grid (z=0); the back wall stands at the grid's far depth edge;
90757
- * the side wall stands at the grid's far width edge, matching the 2D
90758
- * isometric renderer's back-wall/side-wall corner convention.
90759
- *
90760
- * `extent` defaults to the surface chart's own `computeGridExtent(cols, rows)`
90761
- * (1:1 grid-step spacing) when omitted, so existing callers are unaffected.
90762
- * A cartesian scene whose depth axis is NOT 1:1 with its row count (bar3D's
90763
- * `c:view3D/@depthPercent` scaling) passes its own precomputed extent instead
90764
- * of letting this module recompute a mismatched one.
90765
- */
90766
- function buildSurfaceWallMeshes(three, cols, rows, wallHeight, colors, extent) {
90767
- const { gridWidth, gridDepth } = extent ?? computeGridExtent(cols, rows);
90768
- const meshes = [];
90769
- const disposables = [];
90770
- const addPlane = (width, height, color, position, rotation) => {
90771
- const geometry = new three.PlaneGeometry(width, height);
90772
- const material = new three.MeshBasicMaterial({
90773
- color,
90774
- transparent: true,
90775
- opacity: PANEL_OPACITY,
90776
- side: three.DoubleSide,
90777
- depthWrite: false,
90778
- });
90779
- const mesh = new three.Mesh(geometry, material);
90780
- mesh.position.set(...position);
90781
- mesh.rotation.set(...rotation);
90782
- meshes.push(mesh);
90783
- disposables.push(geometry, material);
90784
- };
90785
- if (colors.floor) {
90786
- addPlane(gridWidth, gridDepth, colors.floor, [0, -0.01, 0], [-Math.PI / 2, 0, 0]);
90787
- }
90788
- if (colors.backWall) {
90789
- addPlane(gridWidth, wallHeight, colors.backWall, [0, wallHeight / 2, -gridDepth / 2], [0, 0, 0]);
90790
- }
90791
- if (colors.sideWall) {
90792
- addPlane(gridDepth, wallHeight, colors.sideWall, [gridWidth / 2, wallHeight / 2, 0], [0, Math.PI / 2, 0]);
90793
- }
90794
- return {
90795
- meshes,
90796
- dispose: () => {
90797
- for (const d of disposables) {
90798
- d.dispose();
90799
- }
90800
- },
90801
- };
90802
- }
90803
-
90804
- /**
90805
- * three.js 3D surface-chart scene for `<pptx-three-view>` (framework-agnostic).
90806
- *
90807
- * Builds an interactive surface chart as a `<pptx-three-view>` scene: a
90808
- * colour-displaced surface mesh (with optional wireframe), grid floor,
90809
- * lights, an isometric camera, and DOM axis labels re-projected to screen
90810
- * after each frame. Raycasts pointer moves against the mesh for a native
90811
- * hover tooltip (see {@link ./surface-chart-3d-hit-test.ts}) and clicks/drags
90812
- * for selection and value editing (see
90813
- * {@link ./surface-chart-3d-interaction-wiring.ts}).
90814
- *
90815
- * @module surface-chart-3d-scene
90816
- */
90817
- const FOV = 45;
90818
- /** Build the hosted SurfaceChart3D scene. */
90819
- function createSurfaceChart3DScene(ctx, options) {
90820
- const { cols, rows } = options;
90821
- const placement = computeCameraPlacement(cols, rows, options.view3D);
90822
- const stage = createHostedChart3DStage(ctx, { fov: FOV, ...placement }, { minDistance: 1, maxDistance: 20 }, 0.6);
90823
- const { three, scene, camera, canvas, controls, width, height } = stage;
90824
- const interaction = hostedChart3DInteraction(ctx);
90825
- // Grid floor under the surface.
90826
- const { gridWidth, gridDepth } = computeGridExtent(cols, rows);
90827
- const floorSize = Math.max(gridWidth, gridDepth) * 1.2;
90828
- const gridFloor = new three.GridHelper(floorSize, Math.max(cols, rows), 0xcccccc, 0xe8e8e8);
90829
- gridFloor.position.y = -0.02;
90830
- scene.add(gridFloor);
90831
- const walls = options.surfaceColors
90832
- ? buildSurfaceWallMeshes(three, cols, rows, MAX_HEIGHT, options.surfaceColors)
90833
- : null;
90834
- for (const mesh of walls?.meshes ?? []) {
90835
- scene.add(mesh);
90836
- }
90837
- const { heightMap, colorMap } = options;
90838
- const { geometry, wireGeometry } = buildSurfaceGeometry(three, cols, rows, heightMap, colorMap);
90839
- const surfaceMaterial = new three.MeshPhongMaterial({
90840
- vertexColors: true,
90841
- side: three.DoubleSide,
90842
- shininess: 30,
90843
- transparent: true,
90844
- opacity: 0.92,
90845
- });
90846
- const surfaceMesh = new three.Mesh(geometry, surfaceMaterial);
90847
- scene.add(surfaceMesh);
90848
- // Selected-vertex highlight: see `surface-chart-3d-interaction-wiring.ts`'s
90849
- // module doc for why this is a marker mesh rather than a material tint.
90850
- const highlightMarker = createSurfaceHighlightMarker(three, cols, rows, heightMap);
90851
- scene.add(highlightMarker.mesh);
90852
- let wireMaterial = null;
90853
- if (options.wireframe) {
90854
- wireMaterial = new three.LineBasicMaterial({
90855
- color: 0x333333,
90856
- transparent: true,
90857
- opacity: 0.25,
90858
- });
90859
- scene.add(new three.LineSegments(wireGeometry, wireMaterial));
90860
- }
90861
- // Raycast-based hover tooltip against the single surface mesh (see
90862
- // `surface-chart-3d-hit-test.ts` for the raycast-to-cell math).
90863
- const hoverTooltip = attachChart3DHoverTooltip({
90864
- three,
90865
- canvas,
90866
- camera,
90867
- meshes: [surfaceMesh],
90868
- buildTooltip: (intersection) => buildSurfaceHoverTooltip(intersection?.faceIndex, {
90869
- cols,
90870
- rows,
90871
- categoryLabels: options.categoryLabels,
90872
- seriesNames: options.seriesNames,
90873
- values: options.values,
90874
- numberFormats: options.numberFormats,
90875
- }),
90876
- });
90877
- // Click-to-select + drag-to-value (see surface-chart-3d-interaction-wiring.ts).
90878
- const pointerInteraction = attachSurfaceChart3DInteraction({
90879
- three,
90880
- canvas,
90881
- camera,
90882
- controls,
90883
- width,
90884
- height,
90885
- surfaceMesh,
90886
- cols,
90887
- rows,
90888
- heightMap,
90889
- values: options.values,
90890
- highlightMarker,
90891
- interaction,
90892
- });
90893
- // Axis-label DOM overlay, re-projected to screen each frame.
90894
- const labels = buildSurfaceLabels(cols, rows, options.categoryLabels, options.seriesNames);
90895
- const labelProjector = createChart3DLabelProjector(three, ctx.document, labels);
90896
- labelProjector.applyTextStyle(options.textStyle);
90897
- ctx.overlay.appendChild(labelProjector.layer);
90898
- return finishHostedChart3DScene(ctx, stage, {
90899
- afterRender: (cam, w, h) => labelProjector.update(cam, w, h),
90900
- resize: (w, h) => pointerInteraction.updateSize(w, h),
90901
- setSelectedPart: (part) => pointerInteraction.setSelectedPart(part),
90902
- setTextStyle: (style) => labelProjector.applyTextStyle(style),
90903
- dispose() {
90904
- hoverTooltip.dispose();
90905
- pointerInteraction.dispose();
90906
- geometry.dispose();
90907
- wireGeometry.dispose();
90908
- surfaceMaterial.dispose();
90909
- wireMaterial?.dispose();
90910
- highlightMarker.dispose();
90911
- gridFloor.dispose();
90912
- walls?.dispose();
90913
- labelProjector.layer.remove();
90914
- },
90915
- }, interaction);
90916
- }
90917
-
90918
- function colorFace(color) {
90919
- return { kind: 'color', color };
90920
- }
90921
- /**
90922
- * World-space height one unit of the plotted VALUE spans for this specific
90923
- * box, derived from the box's own already-resolved `size`/`value` (both set
90924
- * by `layoutBarChart3D` from the SAME linear value-axis scale every box in
90925
- * the chart shares). This needs no container/camera measurement at all: unlike
90926
- * a screen-pixel height, `box.size`/`box.value` are fixed the moment the chart
90927
- * is laid out and never change as the user orbits or zooms the scene.
90928
- *
90929
- * `undefined` when `box.value` is `0` (nothing to anchor a "one value unit"
90930
- * height to); the caller falls back to a single tile. Note this is a
90931
- * per-box approximation, not an exact axis-scale lookup: `layoutBarChart3D`
90932
- * clamps a very small bar to a minimum visible height and a non-zero-based
90933
- * value range offsets `box.value`, so the ratio can drift slightly from the
90934
- * chart's true value-per-world-unit scale in those edge cases - the same
90935
- * spirit of "closest available approximation" as this renderer's other
90936
- * documented 3-D-scene gaps (see `chart-bar3d-face-picture.ts`'s module doc).
90937
- */
90938
- function worldHeightPerValueUnit(box) {
90939
- return box.value !== 0 ? box.size[1] / box.value : undefined;
90940
- }
90941
- /**
90942
- * Repeat count (>=1 tile) for `stack`/`stackScale` along one face axis;
90943
- * always `1` for `stretch`. `pictureStackUnit` (`c:pictureStackUnit`) is
90944
- * interpreted here as a VALUE-axis quantity (one tile per that many data
90945
- * units, matching the OOXML spec's own "units per picture" semantics): the
90946
- * number of tiles that exactly fit a box of value `box.value` is then
90947
- * `box.value / pictureStackUnit`, which is exactly what falls out of
90948
- * `faceWorldSize / (pictureStackUnit * worldHeightPerValueUnit(box))` when
90949
- * `faceWorldSize` is `box.size[1]` (see {@link worldHeightPerValueUnit}).
90950
- */
90951
- function faceRepeat(format, pictureStackUnit, faceWorldSize, box) {
90952
- if (format === 'stretch' || pictureStackUnit === undefined || pictureStackUnit <= 0) {
90953
- return 1;
90954
- }
90955
- const perValueUnit = worldHeightPerValueUnit(box);
90956
- if (!perValueUnit) {
90957
- return 1;
90958
- }
90959
- const tileWorldSize = pictureStackUnit * perValueUnit;
90960
- return tileWorldSize > 0 ? Math.max(1, faceWorldSize / tileWorldSize) : 1;
90961
- }
90962
- /** Build the picture-or-colour fill for one named face, resolving its repeat maths from `faceWorldSize`. */
90963
- function resolveTargetedFace(series, box, face, faceWorldSize, untargetedFallback) {
90964
- const resolved = resolveDataPointPictureFill(series, box.categoryIndex, box.seriesIndex, face);
90965
- if (!resolved) {
90966
- return colorFace(untargetedFallback);
90967
- }
90968
- const pictureStackUnit = resolveActiveDataPointPicture(series, box.categoryIndex)?.pictureStackUnit;
90969
- return {
90970
- kind: 'picture',
90971
- imageUrl: resolved.imageUrl,
90972
- repeatX: 1,
90973
- repeatY: faceRepeat(resolved.format, pictureStackUnit, faceWorldSize, box),
90974
- };
90975
- }
90976
- /** `true` (and the shared colour) only when every given face resolved to the SAME plain colour. */
90977
- function allSameColor(fills) {
90978
- const first = fills[0];
90979
- if (!first || first.kind !== 'color') {
90980
- return undefined;
90981
- }
90982
- return fills.every((fill) => fill.kind === 'color' && fill.color === first.color)
90983
- ? first.color
90984
- : undefined;
90985
- }
90986
- /**
90987
- * Resolve one box's six `BoxGeometry` face fills.
90988
- *
90989
- * Returns all six as the box's plain, unmodified colour (no derived tint or
90990
- * shade: the caller relies on real per-face Phong lighting for the 3-D look,
90991
- * unlike the flat SVG renderer's manual shading) when the box is not a `box`
90992
- * shape, or has no picture fill resolved at all - i.e. the pre-existing,
90993
- * unchanged appearance for the overwhelming majority of bar3D charts that
90994
- * never author `c:pictureOptions`.
90995
- *
90996
- * Once a point (or its series) has ANY picture fill configured, this mirrors
90997
- * the flat SVG renderer's own per-face treatment: `front` keeps the plain
90998
- * resolved colour when untargeted (never tinted, matching
90999
- * `chart-datapoint-picture-fills.ts`'s front-face fallback); `side`/`end`
91000
- * fall back to {@link resolveUntargetedBarFaceFill}'s shade/tint when
91001
- * untargeted, matching the SVG oblique-projection renderer's own fallback for
91002
- * those two faces (`chart-3d-depth.ts`). `back` (`-z`) and `bottom` (`-y`)
91003
- * are never modelled by PowerPoint's `applyToFront`/`applyToSides`/
91004
- * `applyToEnd` (they are never visible: bottom sits on the floor, back faces
91005
- * away from the scene's default camera), so they always keep the box's plain
91006
- * resolved colour.
91007
- */
91008
- function resolveBarBoxFaceFills(box, series) {
91009
- const plain = {
91010
- posX: colorFace(box.color),
91011
- negX: colorFace(box.color),
91012
- posY: colorFace(box.color),
91013
- negY: colorFace(box.color),
91014
- posZ: colorFace(box.color),
91015
- negZ: colorFace(box.color),
91016
- };
91017
- if (box.shape !== undefined && box.shape !== 'box') {
91018
- return plain;
91019
- }
91020
- const seriesData = series[box.seriesIndex];
91021
- if (!seriesData) {
91022
- return plain;
91023
- }
91024
- const active = resolveActiveDataPointPicture(seriesData, box.categoryIndex);
91025
- if (!active?.imageUrl) {
91026
- return plain;
91027
- }
91028
- const [, height, depth] = box.size;
91029
- const frontFill = resolveTargetedFace(seriesData, box, 'front', height, box.color);
91030
- const sideFill = resolveTargetedFace(seriesData, box, 'side', height, resolveUntargetedBarFaceFill('side', box.color));
91031
- const endFill = resolveTargetedFace(seriesData, box, 'end', depth, resolveUntargetedBarFaceFill('end', box.color));
91032
- return {
91033
- posX: sideFill,
91034
- negX: sideFill,
91035
- posY: endFill,
91036
- negY: colorFace(box.color),
91037
- posZ: frontFill,
91038
- negZ: colorFace(box.color),
91039
- };
91040
- }
91041
- /**
91042
- * Resolve one round bar's (cylinder/cone/pyramid/coneToMax/pyramidToMax)
91043
- * three material-group fills: `side` (the lateral surface, `applyToSides`),
91044
- * `end` (the top cap, `applyToEnd`) and `bottom` (never targeted by
91045
- * PowerPoint, like a box's `-Y`; always the plain resolved colour). Returns
91046
- * all three plain when the shape has no picture fill resolved at all, same
91047
- * fast path as {@link resolveBarBoxFaceFills}.
91048
- */
91049
- function resolveBarRoundFaceFills(box, series) {
91050
- const plain = {
91051
- side: colorFace(box.color),
91052
- end: colorFace(box.color),
91053
- bottom: colorFace(box.color),
91054
- };
91055
- const seriesData = series[box.seriesIndex];
91056
- if (!seriesData) {
91057
- return plain;
91058
- }
91059
- const active = resolveActiveDataPointPicture(seriesData, box.categoryIndex);
91060
- if (!active?.imageUrl) {
91061
- return plain;
91062
- }
91063
- const [, height, depth] = box.size;
91064
- const sideFill = resolveTargetedFace(seriesData, box, 'side', height, resolveUntargetedBarFaceFill('side', box.color));
91065
- const endFill = resolveTargetedFace(seriesData, box, 'end', depth, resolveUntargetedBarFaceFill('end', box.color));
91066
- return { side: sideFill, end: endFill, bottom: colorFace(box.color) };
91067
- }
91068
- /** `true` (and the shared colour) when a resolved {@link BarBoxFaceFills} is uniform across all six faces. */
91069
- function uniformBoxColor(fills) {
91070
- return allSameColor([fills.posX, fills.negX, fills.posY, fills.negY, fills.posZ, fills.negZ]);
91071
- }
91072
- /** `true` (and the shared colour) when a resolved {@link BarRoundFaceFills} is uniform across all three material groups. */
91073
- function uniformRoundColor(fills) {
91074
- return allSameColor([fills.side, fills.end, fills.bottom]);
91075
- }
91076
-
91077
- /** Create a texture manager backed by `three.TextureLoader`, scoped to one mounted scene. */
91078
- function createBarChart3DTextureManager(three) {
91079
- const loader = new three.TextureLoader();
91080
- const cache = new Map();
91081
- const loaded = [];
91082
- return {
91083
- load(url) {
91084
- const cached = cache.get(url);
91085
- if (cached) {
91086
- return cached;
91087
- }
91088
- const pending = new Promise((resolve, reject) => {
91089
- loader.load(url, (texture) => {
91090
- loaded.push(texture);
91091
- resolve(texture);
91092
- }, undefined, reject);
91093
- });
91094
- cache.set(url, pending);
91095
- return pending;
91096
- },
91097
- disposeAll() {
91098
- for (const texture of loaded) {
91099
- texture.dispose();
91100
- }
91101
- loaded.length = 0;
91102
- cache.clear();
91103
- },
91104
- };
91105
- }
91106
- const FACE_ORDER = [
91107
- 'posX',
91108
- 'negX',
91109
- 'posY',
91110
- 'negY',
91111
- 'posZ',
91112
- 'negZ',
91113
- ];
91114
- function buildColorMaterial(three, color) {
91115
- return new three.MeshPhongMaterial({ color, shininess: 30 });
91116
- }
91117
- /**
91118
- * Build one `BoxGeometry` face's material. A picture face mounts a plain
91119
- * white `MeshPhongMaterial` (white so `map * color` paints the picture
91120
- * unmodified once it lands, matching the SVG `<pattern>` fill) and swaps its
91121
- * `map` in once the shared texture manager resolves the image, cloning the
91122
- * base texture so this face's own `repeat` never affects a sibling face or
91123
- * box sharing the same underlying image. `isDisposed` guards the async
91124
- * continuation against a box disposed (e.g. the chart unmounted, or the deck
91125
- * navigated away) before its texture finished loading.
91126
- */
91127
- function buildFaceMaterial(three, fill, textures, ownedTextures, isDisposed) {
91128
- if (fill.kind === 'color') {
91129
- return buildColorMaterial(three, fill.color);
91130
- }
91131
- const material = buildColorMaterial(three, '#ffffff');
91132
- textures
91133
- .load(fill.imageUrl)
91134
- .then((base) => {
91135
- const texture = base.clone();
91136
- texture.wrapS = three.RepeatWrapping;
91137
- texture.wrapT = three.RepeatWrapping;
91138
- texture.repeat.set(fill.repeatX, fill.repeatY);
91139
- texture.needsUpdate = true;
91140
- if (isDisposed()) {
91141
- texture.dispose();
91142
- return undefined;
91143
- }
91144
- ownedTextures.push(texture);
91145
- material.map = texture;
91146
- material.needsUpdate = true;
91147
- return undefined;
91148
- })
91149
- .catch(() => {
91150
- // Image failed to load/decode: keep the white fallback material
91151
- // rather than throwing out of an async continuation, matching every
91152
- // other optional-asset load in this renderer.
91153
- });
91154
- return material;
91155
- }
91156
- /** Build a `BarBoxMaterialResult` from an already-resolved fill array, one material per array entry (matching order = matching `THREE` `materialIndex`). */
91157
- function buildMaterialArrayResult(three, fills, textures) {
91158
- let disposed = false;
91159
- const ownedTextures = [];
91160
- const materials = fills.map((fill) => buildFaceMaterial(three, fill, textures, ownedTextures, () => disposed));
91161
- return {
91162
- material: materials,
91163
- dispose: () => {
91164
- disposed = true;
91165
- for (const material of materials) {
91166
- material.dispose();
91167
- }
91168
- for (const texture of ownedTextures) {
91169
- texture.dispose();
91170
- }
91171
- },
91172
- };
91173
- }
91174
- /**
91175
- * Build the material(s) for one bar3D mesh. `assets` is `undefined` for a
91176
- * scene mounted with no series/picture context (every pre-existing caller),
91177
- * which keeps the original single uniform `MeshPhongMaterial` behaviour
91178
- * unchanged. When `assets` is given: a `box`-shaped bar resolves
91179
- * {@link resolveBarBoxFaceFills} (up to a 6-entry `BoxGeometry` face-material
91180
- * array); a round shape (cylinder/cone/pyramid/coneToMax/pyramidToMax)
91181
- * resolves {@link resolveBarRoundFaceFills} (up to a 3-entry
91182
- * `CylinderGeometry`/`ConeGeometry` material-group array: lateral surface,
91183
- * top cap, bottom cap - see this module's `BarRoundFaceFills` import for the
91184
- * `materialIndex` order this array must match). Either shape returns a
91185
- * single uniform material instead when no picture targets any of its faces
91186
- * (the common no-picture case), same as before per-face targeting existed.
91187
- */
91188
- function buildBarBoxMaterial(three, box, assets) {
91189
- if (!assets) {
91190
- const material = buildColorMaterial(three, box.color);
91191
- return { material, dispose: () => material.dispose() };
91192
- }
91193
- const isRound = box.shape !== undefined && box.shape !== 'box';
91194
- if (isRound) {
91195
- const roundFills = resolveBarRoundFaceFills(box, assets.context.series);
91196
- const solid = uniformRoundColor(roundFills);
91197
- if (solid !== undefined) {
91198
- const material = buildColorMaterial(three, solid);
91199
- return { material, dispose: () => material.dispose() };
91200
- }
91201
- return buildMaterialArrayResult(three, [roundFills.side, roundFills.end, roundFills.bottom], assets.textures);
91202
- }
91203
- const fills = resolveBarBoxFaceFills(box, assets.context.series);
91204
- const solid = uniformBoxColor(fills);
91205
- if (solid !== undefined) {
91206
- const material = buildColorMaterial(three, solid);
91207
- return { material, dispose: () => material.dispose() };
91208
- }
91209
- return buildMaterialArrayResult(three, FACE_ORDER.map((face) => fills[face]), assets.textures);
91210
- }
91211
-
91212
- /**
91213
- * Per-shape THREE geometry construction for the interactive bar3D scene
91214
- * ({@link ./bar-chart-3d-scene.ts}), keyed by the resolved
91215
- * `c:bar3DChart/c:shape` (or per-series `c:ser/c:shape` override).
91216
- *
91217
- * Every geometry is a UNIT mesh (spans -0.5..0.5 on every axis, matching
91218
- * `THREE.BoxGeometry(1, 1, 1)`'s own extent), so the caller's existing
91219
- * `mesh.scale.set(...box.size)` keeps working unchanged regardless of shape:
91220
- * this module only decides WHICH geometry, never the box's world-space size
91221
- * or position.
91222
- *
91223
- * `coneToMax` / `pyramidToMax` render as a truncated cone/pyramid
91224
- * (`radiusTop < radiusBottom`) using
91225
- * {@link BarChart3DBox.coneToMaxTopRadiusFactor}, so a bar far below the
91226
- * value-axis maximum looks like a wide near-cylindrical frustum and a bar at
91227
- * the maximum comes to a full point, matching every box being read as a
91228
- * slice of one imaginary cone/pyramid whose apex sits at the axis max.
91229
- *
91230
- * @module bar-chart-3d-geometry
91231
- */
91232
- /** Radial segment count for a full-round shape (cylinder / cone). */
91233
- const ROUND_SEGMENTS = 32;
91234
- /** Radial segment count for a 4-sided shape (pyramid), approximating a square base. */
91235
- const PYRAMID_SEGMENTS = 4;
91236
- /**
91237
- * Build the unit-size geometry for one bar3D box, keyed by its resolved
91238
- * shape. `box` (or an unrecognised/absent shape) keeps the original
91239
- * `BoxGeometry(1, 1, 1)` behaviour.
91240
- */
91241
- function buildBar3DGeometry(three, shape, coneToMaxTopRadiusFactor) {
91242
- switch (shape) {
91243
- case 'cylinder':
91244
- return new three.CylinderGeometry(0.5, 0.5, 1, ROUND_SEGMENTS);
91245
- case 'cone':
91246
- return new three.ConeGeometry(0.5, 1, ROUND_SEGMENTS);
91247
- case 'pyramid':
91248
- return new three.ConeGeometry(0.5, 1, PYRAMID_SEGMENTS);
91249
- case 'coneToMax':
91250
- return new three.CylinderGeometry(0.5 * (coneToMaxTopRadiusFactor ?? 0), 0.5, 1, ROUND_SEGMENTS);
91251
- case 'pyramidToMax':
91252
- return new three.CylinderGeometry(0.5 * (coneToMaxTopRadiusFactor ?? 0), 0.5, 1, PYRAMID_SEGMENTS);
91253
- case 'box':
91254
- default:
91255
- return new three.BoxGeometry(1, 1, 1);
91256
- }
91257
- }
91258
- /**
91259
- * Build and add one mesh per box to `scene`, geometry chosen per box's
91260
- * resolved shape (see {@link buildBar3DGeometry}). Distinct shapes need
91261
- * distinct THREE geometry instances (cone/pyramid share a class but differ by
91262
- * radial segment count), so geometries are built per box rather than shared,
91263
- * and returned for the caller to dispose individually on teardown.
91264
- *
91265
- * `horizontal` (`c:barDir val="bar"`) rotates every mesh `-Math.PI / 2` about
91266
- * Z: `box.center` already sits in the horizontal world frame
91267
- * (`layoutBarChart3D`), but `box.size` stays in the UNROTATED local frame
91268
- * (width/height/depth as if the chart were still vertical), so a
91269
- * cylinder/cone/pyramid keeps its true round cross-section lying on its side
91270
- * instead of the ellipse a numeric width/height swap would produce. A plain
91271
- * box shape looks identical either way (rotating a rectangular prism 90
91272
- * degrees is the same as swapping two of its extents).
91273
- *
91274
- * `picture`, when given, resolves each box's `c:pictureOptions` picture-fill
91275
- * face targeting via {@link buildBarBoxMaterial}; omitted entirely, every box
91276
- * keeps its original single uniform `MeshPhongMaterial(box.color)`.
91277
- */
91278
- function buildBar3DMeshGroup(three, scene, boxes, horizontal = false, picture) {
91279
- const meshes = [];
91280
- const materials = [];
91281
- const materialDisposers = [];
91282
- const geometries = [];
91283
- for (const box of boxes) {
91284
- const geometry = buildBar3DGeometry(three, box.shape, box.coneToMaxTopRadiusFactor);
91285
- const { material, dispose } = buildBarBoxMaterial(three, box, picture);
91286
- const mesh = new three.Mesh(geometry, material);
91287
- mesh.position.set(...box.center);
91288
- mesh.scale.set(...box.size);
91289
- if (horizontal) {
91290
- mesh.rotation.z = -Math.PI / 2;
91291
- }
91292
- mesh.userData = {
91293
- seriesIndex: box.seriesIndex,
91294
- categoryIndex: box.categoryIndex,
91295
- value: box.value,
91296
- };
91297
- scene.add(mesh);
91298
- meshes.push(mesh);
91299
- materials.push(material);
91300
- materialDisposers.push(dispose);
91301
- geometries.push(geometry);
91302
- }
91303
- return { meshes, materials, geometries, materialDisposers };
91304
- }
91305
-
91306
89310
  /**
91307
89311
  * Pure raycast-hit -> hover-tooltip mapping for the interactive 3D bar chart
91308
89312
  * scene ({@link ./bar-chart-3d-scene.ts}).
@@ -91332,978 +89336,6 @@ function buildBarChart3DHoverTooltip(hit, data) {
91332
89336
  return buildMarkTooltip(data.seriesNames[hit.seriesIndex], data.categoryLabels[hit.categoryIndex], hit.value, data.numberFormats?.[hit.seriesIndex]);
91333
89337
  }
91334
89338
 
91335
- /** World-space axis index the value axis occupies: Y normally, X for a horizontal (`c:barDir val="bar"`) chart. */
91336
- function valueAxisIndex(horizontal) {
91337
- return horizontal ? 0 : 1;
91338
- }
91339
- /**
91340
- * Calibrate a value drag for the box a raycast hit landed on, or `null` when
91341
- * the chart's grouping has no per-box "spans from zero" shape, the matching
91342
- * box cannot be found, or the box has (near) zero extent along the value axis.
91343
- */
91344
- function calibrateBarChart3DDrag(boxes, hit, grouping, horizontal) {
91345
- if (grouping !== 'clustered') {
91346
- return null;
91347
- }
91348
- const box = boxes.find((b) => b.seriesIndex === hit.seriesIndex && b.categoryIndex === hit.categoryIndex);
91349
- if (!box) {
91350
- return null;
91351
- }
91352
- const axis = valueAxisIndex(horizontal);
91353
- const half = box.size[axis] / 2;
91354
- const center = box.center[axis];
91355
- const lo = center - half;
91356
- const hi = center + half;
91357
- // One of the two faces sits at world value-zero (see module doc); the
91358
- // closer-to-zero extreme is that face, the other is the box's own value.
91359
- const zeroIsLo = Math.abs(lo) <= Math.abs(hi);
91360
- const zeroCoord = zeroIsLo ? lo : hi;
91361
- const valueCoord = zeroIsLo ? hi : lo;
91362
- if (Math.abs(valueCoord - zeroCoord) < 1e-9) {
91363
- return null;
91364
- }
91365
- const at = (coord) => axis === 0 ? [coord, box.center[1], box.center[2]] : [box.center[0], coord, box.center[2]];
91366
- return {
91367
- worldAtValue0: at(zeroCoord),
91368
- value0: 0,
91369
- worldAtValue1: at(valueCoord),
91370
- value1: hit.value,
91371
- };
91372
- }
91373
-
91374
- /**
91375
- * `chart-3d-mesh-highlight`: the one-mesh-per-mark selected-highlight loop
91376
- * shared by bar3D, line3D, area3D, and pie3D (every interactive 3D chart
91377
- * scene EXCEPT surface3D, whose grid is a single mesh with no per-cell
91378
- * material to isolate - it highlights its selected vertex with a small
91379
- * marker mesh instead, see `surface-chart-3d-interaction-wiring.ts`'s
91380
- * `createSurfaceHighlightMarker`).
91381
- *
91382
- * Each of those four scenes tags every mark's own mesh material with its
91383
- * (series, point) in `userData` already (for hover/click hit-testing); this
91384
- * module just walks that same list and toggles `material.emissive` on the
91385
- * mark matching the current selection, clearing every other mark's.
91386
- *
91387
- * A bar3D box with a `c:pictureOptions` picture fill mounts a per-face
91388
- * `THREE.Material[]` array rather than one material (see
91389
- * `bar-chart-3d-materials.ts`), so `material` here also accepts a readonly
91390
- * array: every material in it gets the same emissive toggle, highlighting
91391
- * (or clearing) the whole box regardless of how many materials its mesh
91392
- * carries.
91393
- *
91394
- * @module chart-3d-mesh-highlight
91395
- */
91396
- const CLEAR_EMISSIVE = '#000000';
91397
- /**
91398
- * Apply (or clear) the selected-mark emissive highlight across a scene's
91399
- * mark meshes. `entries` pairs each mesh's own (series, point) reference with
91400
- * its material (or, for a multi-material mesh, every material in its
91401
- * per-face array); every entry not matching `part` is reset to no highlight.
91402
- */
91403
- function applyChart3DMeshHighlight(entries, part) {
91404
- for (const { mark, material } of entries) {
91405
- const selected = chart3DMarkMatchesPart(mark, part);
91406
- const materials = Array.isArray(material) ? material : [material];
91407
- for (const one of materials) {
91408
- one.emissive.set(selected ? CHART_3D_SELECTED_EMISSIVE : CLEAR_EMISSIVE);
91409
- one.emissiveIntensity = selected ? CHART_3D_SELECTED_EMISSIVE_INTENSITY : 0;
91410
- }
91411
- }
91412
- }
91413
-
91414
- /** Attach click-to-select / drag-to-value handling to a mounted bar3D scene. */
91415
- function attachBarChart3DInteraction(params) {
91416
- const highlightEntries = params.boxMeshes.map((mesh, i) => {
91417
- const hit = mesh.userData;
91418
- return {
91419
- mark: { seriesIndex: hit.seriesIndex, pointIndex: hit.categoryIndex },
91420
- material: params.boxMaterials[i],
91421
- };
91422
- });
91423
- return attachChart3DPointerInteraction({
91424
- three: params.three,
91425
- canvas: params.canvas,
91426
- camera: params.camera,
91427
- width: params.width,
91428
- height: params.height,
91429
- meshes: params.boxMeshes,
91430
- resolveHit: (intersection) => {
91431
- const hit = intersection.object.userData;
91432
- return chart3DHitToPartRef(hit ? { seriesIndex: hit.seriesIndex, pointIndex: hit.categoryIndex } : null);
91433
- },
91434
- calibrateDrag: (intersection) => {
91435
- const hit = intersection.object.userData;
91436
- return calibrateBarChart3DDrag(params.boxes, hit, params.grouping, params.horizontal);
91437
- },
91438
- applyHighlight: (part) => applyChart3DMeshHighlight(highlightEntries, part),
91439
- setControlsEnabled: (enabled) => {
91440
- params.controls.enabled = enabled;
91441
- },
91442
- onSelect: params.interaction?.onSelect,
91443
- onValueDragPreview: params.interaction?.onValueDragPreview,
91444
- onValueDragCommit: params.interaction?.onValueDragCommit,
91445
- });
91446
- }
91447
-
91448
- /**
91449
- * three.js 3D bar-chart scene for `<pptx-three-view>` (framework-agnostic).
91450
- *
91451
- * Builds an interactive `bar3D` chart as a `<pptx-three-view>` scene
91452
- * (hosted on the shared renderer; see {@link ./chart-3d-hosted-stage.ts}): builds
91453
- * one mesh per data point (clustered: each series its own depth plane;
91454
- * stacked/percentStacked: coplanar, stacked vertically - see
91455
- * {@link ./bar-chart-3d-data.ts}; geometry per box's resolved `c:shape` - see
91456
- * {@link ./bar-chart-3d-geometry.ts}), a grid floor, authored `c:floor`/
91457
- * `c:sideWall`/`c:backWall` panels and a perspective camera driven by
91458
- * `c:view3D`. Raycasts pointer moves against the boxes to
91459
- * set a native hover tooltip on the canvas element (see
91460
- * {@link ./bar-chart-3d-hit-test.ts}), matching every other chart kind's
91461
- * SVG-`<title>` hover tooltip.
91462
- *
91463
- * A horizontal 3-D Bar (`options.horizontal`) mounts the SAME scene: boxes
91464
- * arrive already remapped into the horizontal frame, so this module only
91465
- * rotates each mesh to match, builds the label overlay in that frame, and
91466
- * asks {@link computeCartesianCameraPlacement} for the matching (also
91467
- * transposed) camera placement so the view frames the actual rendered
91468
- * extents instead of the untransposed vertical bounding box.
91469
- *
91470
- * @module bar-chart-3d-scene
91471
- */
91472
- /** Build the hosted bar3D scene. */
91473
- function createBarChart3DScene(ctx, options) {
91474
- const placement = computeCartesianCameraPlacement(options.cols, options.rows, options.view3D, options.horizontal);
91475
- const stage = createHostedChart3DStage(ctx, placement, { minDistance: 0.5, maxDistance: 30 });
91476
- const { three, scene, camera, canvas } = stage;
91477
- // Grid floor, sized to this module's OWN grid extent (depthPercent-scaled).
91478
- const extent = computeCartesianGridExtent(options.cols, options.rows, options.view3D?.depthPercent);
91479
- // A horizontal bar3D chart's boxes sit at various world-Y heights (the
91480
- // remapped category axis), so the floor also covers that vertical spread.
91481
- const floorSize = Math.max(extent.gridWidth, extent.gridDepth, MAX_VALUE_HEIGHT * 2) * 1.2;
91482
- const gridFloor = new three.GridHelper(floorSize, Math.max(options.cols, options.rows), 0xcccccc, 0xe8e8e8);
91483
- gridFloor.position.y = -0.02;
91484
- scene.add(gridFloor);
91485
- // Authored c:floor / c:sideWall / c:backWall backdrop panels. Passes this
91486
- // module's OWN grid extent, never letting buildSurfaceWallMeshes recompute
91487
- // a mismatched one. KNOWN APPROXIMATION: unlike the boxes and labels, these
91488
- // panels are not reoriented for `options.horizontal` (they still paint the
91489
- // vertical category=X, value=Y frame's panel positions).
91490
- const walls = options.wallColors
91491
- ? buildSurfaceWallMeshes(three, options.cols, options.rows, MAX_VALUE_HEIGHT, options.wallColors, extent)
91492
- : null;
91493
- for (const mesh of walls?.meshes ?? []) {
91494
- scene.add(mesh);
91495
- }
91496
- // One mesh per data point, geometry chosen per box's resolved bar3D shape
91497
- // (see {@link ./bar-chart-3d-geometry.ts}). `c:pictureOptions` picture-fill
91498
- // textures share one texture manager across every box; each finished load
91499
- // asks the host for a frame, since nothing else would redraw the view.
91500
- const baseTextures = createBarChart3DTextureManager(three);
91501
- const textures = {
91502
- load: (url) => baseTextures.load(url).then((texture) => {
91503
- setTimeout(ctx.requestRender, 0);
91504
- return texture;
91505
- }),
91506
- disposeAll: () => baseTextures.disposeAll(),
91507
- };
91508
- const { meshes: boxMeshes, materials: boxMaterials, geometries: boxGeometries, materialDisposers: boxMaterialDisposers, } = buildBar3DMeshGroup(three, scene, options.boxes, options.horizontal, options.picture ? { context: options.picture, textures } : undefined);
91509
- // Raycast-based hover tooltip: each box mesh carries its own (series,
91510
- // category, value) in `userData`, so a hit reports the cell directly.
91511
- const hoverTooltip = attachChart3DHoverTooltip({
91512
- three,
91513
- canvas,
91514
- camera,
91515
- meshes: boxMeshes,
91516
- buildTooltip: (intersection) => buildBarChart3DHoverTooltip(intersection?.object.userData, {
91517
- categoryLabels: options.categoryLabels,
91518
- seriesNames: options.seriesNames,
91519
- numberFormats: options.numberFormats,
91520
- }),
91521
- });
91522
- // Click-to-select + drag-to-value: raycasts the SAME box meshes the hover
91523
- // tooltip above uses (see bar-chart-3d-interaction-wiring.ts).
91524
- const interaction = hostedChart3DInteraction(ctx);
91525
- const pointerInteraction = attachBarChart3DInteraction({
91526
- three,
91527
- canvas,
91528
- camera,
91529
- controls: stage.controls,
91530
- width: stage.width,
91531
- height: stage.height,
91532
- boxMeshes,
91533
- boxMaterials: boxMaterials,
91534
- boxes: options.boxes,
91535
- grouping: options.grouping,
91536
- horizontal: Boolean(options.horizontal),
91537
- interaction,
91538
- });
91539
- // Axis-label DOM overlay, re-projected to screen after each frame.
91540
- const labels = buildCartesianChart3DLabels(options.cols, options.rows, options.categoryLabels, options.seriesNames, options.view3D?.depthPercent, undefined, undefined, options.horizontal);
91541
- const labelProjector = createChart3DLabelProjector(three, ctx.document, labels);
91542
- labelProjector.applyTextStyle(options.textStyle);
91543
- ctx.overlay.appendChild(labelProjector.layer);
91544
- return finishHostedChart3DScene(ctx, stage, {
91545
- afterRender: (cam, width, height) => labelProjector.update(cam, width, height),
91546
- resize: (width, height) => pointerInteraction.updateSize(width, height),
91547
- setSelectedPart: (part) => pointerInteraction.setSelectedPart(part),
91548
- setTextStyle: (style) => labelProjector.applyTextStyle(style),
91549
- dispose() {
91550
- hoverTooltip.dispose();
91551
- pointerInteraction.dispose();
91552
- for (const g of boxGeometries) {
91553
- g.dispose();
91554
- }
91555
- for (const disposeMaterial of boxMaterialDisposers) {
91556
- disposeMaterial();
91557
- }
91558
- textures.disposeAll();
91559
- gridFloor.dispose();
91560
- walls?.dispose();
91561
- labelProjector.layer.remove();
91562
- },
91563
- }, interaction);
91564
- }
91565
-
91566
- /**
91567
- * Pure raycast-hit -> hover-tooltip mapping shared by the interactive
91568
- * line3D/area3D cartesian chart scenes ({@link ./line-chart-3d-scene.ts},
91569
- * {@link ./area-chart-3d-scene.ts}).
91570
- *
91571
- * Each series path's data-point marker mesh tags its Object3D `userData` with
91572
- * the (series, category, value) triple a raycast hit reports directly,
91573
- * exactly like a bar3D box mesh does (see `bar-chart-3d-hit-test.ts`, the
91574
- * original of this pattern). This module only builds the tooltip text from
91575
- * those indices, kept three-agnostic so it is unit-testable without mocking
91576
- * WebGL.
91577
- *
91578
- * @module cartesian-chart-3d-hit-test
91579
- */
91580
- /**
91581
- * Build the hover-tooltip text for a raycast hit on a line3D/area3D data-point
91582
- * marker, or `undefined` when there is no hit.
91583
- *
91584
- * Mirrors `buildMarkTooltip`'s "<series>, <category>: <value>" text exactly,
91585
- * so a line3D/area3D marker's tooltip reads identically to the flat SVG
91586
- * line/area renderer's, and to `buildBarChart3DHoverTooltip`'s.
91587
- */
91588
- function buildCartesianChart3DHoverTooltip(hit, data) {
91589
- if (!hit) {
91590
- return undefined;
91591
- }
91592
- return buildMarkTooltip(data.seriesNames[hit.seriesIndex], data.categoryLabels[hit.categoryIndex], hit.value, data.numberFormats?.[hit.seriesIndex]);
91593
- }
91594
-
91595
- /**
91596
- * Calibrate a value drag for one line3D/area3D marker, given its world
91597
- * position (value axis = world Y) and its own data value. Returns `null` when
91598
- * the value is (near) zero: the marker then sits on the floor itself, so the
91599
- * two calibration points would coincide and no direction could be recovered.
91600
- */
91601
- function calibrateCartesianChart3DDrag(markerPosition, value) {
91602
- if (Math.abs(value) < 1e-9) {
91603
- return null;
91604
- }
91605
- return {
91606
- worldAtValue0: [markerPosition[0], 0, markerPosition[2]],
91607
- value0: 0,
91608
- worldAtValue1: markerPosition,
91609
- value1: value,
91610
- };
91611
- }
91612
-
91613
- /** Attach click-to-select / drag-to-value handling to a mounted line3D/area3D scene. */
91614
- function attachCartesianChart3DInteraction(params) {
91615
- const highlightEntries = params.markerMeshes.map((mesh, i) => {
91616
- const hit = mesh.userData;
91617
- return {
91618
- mark: { seriesIndex: hit.seriesIndex, pointIndex: hit.categoryIndex },
91619
- material: params.markerMaterials[i],
91620
- };
91621
- });
91622
- return attachChart3DPointerInteraction({
91623
- three: params.three,
91624
- canvas: params.canvas,
91625
- camera: params.camera,
91626
- width: params.width,
91627
- height: params.height,
91628
- meshes: params.markerMeshes,
91629
- resolveHit: (intersection) => {
91630
- const hit = intersection.object.userData;
91631
- return chart3DHitToPartRef(hit ? { seriesIndex: hit.seriesIndex, pointIndex: hit.categoryIndex } : null);
91632
- },
91633
- calibrateDrag: (intersection) => {
91634
- const hit = intersection.object.userData;
91635
- const vertex = params.series[hit.seriesIndex]?.vertices.find((v) => v.categoryIndex === hit.categoryIndex);
91636
- return vertex ? calibrateCartesianChart3DDrag(vertex.position, hit.value) : null;
91637
- },
91638
- applyHighlight: (part) => applyChart3DMeshHighlight(highlightEntries, part),
91639
- setControlsEnabled: (enabled) => {
91640
- params.controls.enabled = enabled;
91641
- },
91642
- onSelect: params.interaction?.onSelect,
91643
- onValueDragPreview: params.interaction?.onValueDragPreview,
91644
- onValueDragCommit: params.interaction?.onValueDragCommit,
91645
- });
91646
- }
91647
-
91648
- /**
91649
- * three.js 3D line-chart scene for `<pptx-three-view>` (framework-agnostic).
91650
- *
91651
- * Builds an interactive `line3D` chart as a `<pptx-three-view>` scene: one
91652
- * `THREE.TubeGeometry` per series (swept along a `CatmullRomCurve3` through
91653
- * its category/value points, own depth "Z" plane, see
91654
- * {@link ./cartesian-line-chart-3d-layout.ts}), a small sphere marker per data
91655
- * point (hover + click/drag hit target), grid floor, authored wall panels, a
91656
- * `c:view3D` camera and OrbitControls, on the shared stage from
91657
- * {@link ./chart-3d-hosted-stage.ts}.
91658
- *
91659
- * @module line-chart-3d-scene
91660
- */
91661
- /** World-space radius of the tube swept along a series' path. */
91662
- const TUBE_RADIUS$1 = 0.025;
91663
- /** World-space radius of each per-vertex hover marker. */
91664
- const MARKER_RADIUS$1 = 0.045;
91665
- /** Build the hosted LineChart3D scene. */
91666
- function createLineChart3DScene(ctx, options) {
91667
- const placement = computeCartesianCameraPlacement(options.cols, options.rows, options.view3D);
91668
- const stage = createHostedChart3DStage(ctx, placement, { minDistance: 0.5, maxDistance: 30 }, 0.6);
91669
- const { three, scene, camera, canvas, controls, width, height } = stage;
91670
- const interaction = hostedChart3DInteraction(ctx);
91671
- const extent = computeCartesianGridExtent(options.cols, options.rows, options.view3D?.depthPercent);
91672
- const floorSize = Math.max(extent.gridWidth, extent.gridDepth) * 1.2;
91673
- const gridFloor = new three.GridHelper(floorSize, Math.max(options.cols, options.rows), 0xcccccc, 0xe8e8e8);
91674
- gridFloor.position.y = -0.02;
91675
- scene.add(gridFloor);
91676
- const walls = options.wallColors
91677
- ? buildSurfaceWallMeshes(three, options.cols, options.rows, MAX_VALUE_HEIGHT, options.wallColors, extent)
91678
- : null;
91679
- for (const mesh of walls?.meshes ?? []) {
91680
- scene.add(mesh);
91681
- }
91682
- // One tube per series; one shared sphere geometry for the hover markers.
91683
- const markerGeometry = new three.SphereGeometry(MARKER_RADIUS$1, 8, 6);
91684
- const tubeGeometries = [];
91685
- const tubeMaterials = [];
91686
- const markerMeshes = [];
91687
- const markerMaterials = [];
91688
- for (const path of options.series) {
91689
- if (path.vertices.length >= 2) {
91690
- const curve = new three.CatmullRomCurve3(path.vertices.map((v) => new three.Vector3(...v.position)));
91691
- const tubeGeometry = new three.TubeGeometry(curve, Math.max(path.vertices.length * 8, 16), TUBE_RADIUS$1, 8, false);
91692
- const tubeMaterial = new three.MeshPhongMaterial({ color: path.color, shininess: 30 });
91693
- const tube = new three.Mesh(tubeGeometry, tubeMaterial);
91694
- scene.add(tube);
91695
- tubeGeometries.push(tubeGeometry);
91696
- tubeMaterials.push(tubeMaterial);
91697
- }
91698
- for (const v of path.vertices) {
91699
- const markerMaterial = new three.MeshPhongMaterial({ color: path.color, shininess: 30 });
91700
- const marker = new three.Mesh(markerGeometry, markerMaterial);
91701
- marker.position.set(...v.position);
91702
- marker.userData = {
91703
- seriesIndex: v.seriesIndex,
91704
- categoryIndex: v.categoryIndex,
91705
- value: v.value,
91706
- };
91707
- scene.add(marker);
91708
- markerMeshes.push(marker);
91709
- markerMaterials.push(markerMaterial);
91710
- }
91711
- }
91712
- // Raycast-based hover tooltip against the per-vertex marker meshes.
91713
- const tooltipData = {
91714
- categoryLabels: options.categoryLabels,
91715
- seriesNames: options.seriesNames,
91716
- numberFormats: options.numberFormats,
91717
- };
91718
- const hoverTooltip = attachChart3DHoverTooltip({
91719
- three,
91720
- canvas,
91721
- camera,
91722
- meshes: markerMeshes,
91723
- buildTooltip: (intersection) => buildCartesianChart3DHoverTooltip(intersection?.object.userData, tooltipData),
91724
- });
91725
- // Click-to-select + drag-to-value (see cartesian-chart-3d-interaction-wiring.ts).
91726
- const pointerInteraction = attachCartesianChart3DInteraction({
91727
- three,
91728
- canvas,
91729
- camera,
91730
- controls,
91731
- width,
91732
- height,
91733
- markerMeshes,
91734
- markerMaterials: markerMaterials,
91735
- series: options.series,
91736
- interaction,
91737
- });
91738
- const labels = buildCartesianChart3DLabels(options.cols, options.rows, options.categoryLabels, options.seriesNames, options.view3D?.depthPercent);
91739
- const labelProjector = createChart3DLabelProjector(three, ctx.document, labels);
91740
- labelProjector.applyTextStyle(options.textStyle);
91741
- ctx.overlay.appendChild(labelProjector.layer);
91742
- return finishHostedChart3DScene(ctx, stage, {
91743
- afterRender: (cam, w, h) => labelProjector.update(cam, w, h),
91744
- resize: (w, h) => pointerInteraction.updateSize(w, h),
91745
- setSelectedPart: (part) => pointerInteraction.setSelectedPart(part),
91746
- setTextStyle: (style) => labelProjector.applyTextStyle(style),
91747
- dispose() {
91748
- hoverTooltip.dispose();
91749
- pointerInteraction.dispose();
91750
- markerGeometry.dispose();
91751
- for (const g of tubeGeometries) {
91752
- g.dispose();
91753
- }
91754
- for (const m of [...tubeMaterials, ...markerMaterials]) {
91755
- m.dispose();
91756
- }
91757
- gridFloor.dispose();
91758
- walls?.dispose();
91759
- labelProjector.layer.remove();
91760
- },
91761
- }, interaction);
91762
- }
91763
-
91764
- /** Build a ribbon `BufferGeometry` for one series' area fill, or `null` when it has < 2 vertices. */
91765
- function buildAreaRibbonGeometry(three, path) {
91766
- const triangles = buildAreaRibbonTriangles(path);
91767
- if (triangles.length === 0) {
91768
- return null;
91769
- }
91770
- const geometry = new three.BufferGeometry();
91771
- geometry.setAttribute('position', new three.Float32BufferAttribute(triangles, 3));
91772
- geometry.computeVertexNormals();
91773
- return geometry;
91774
- }
91775
-
91776
- /**
91777
- * three.js 3D area-chart scene for `<pptx-three-view>` (framework-agnostic).
91778
- *
91779
- * Identical to {@link ./line-chart-3d-scene.ts}, EXCEPT it additionally fills
91780
- * a translucent ribbon from each series' path down to its baseline (value =
91781
- * 0), via {@link ./area-chart-3d-ribbon-geometry.ts}, matching PowerPoint's
91782
- * real 3-D Area chart.
91783
- *
91784
- * @module area-chart-3d-scene
91785
- */
91786
- /** World-space radius of the tube swept along a series' path (drawn atop the ribbon). */
91787
- const TUBE_RADIUS = 0.02;
91788
- /** World-space radius of each per-vertex hover marker. */
91789
- const MARKER_RADIUS = 0.045;
91790
- /** Ribbon fill opacity, translucent so overlapping series planes stay legible. */
91791
- const RIBBON_OPACITY = 0.75;
91792
- /** Build the hosted AreaChart3D scene. */
91793
- function createAreaChart3DScene(ctx, options) {
91794
- const placement = computeCartesianCameraPlacement(options.cols, options.rows, options.view3D);
91795
- const stage = createHostedChart3DStage(ctx, placement, { minDistance: 0.5, maxDistance: 30 }, 0.6);
91796
- const { three, scene, camera, canvas, controls, width, height } = stage;
91797
- const interaction = hostedChart3DInteraction(ctx);
91798
- const extent = computeCartesianGridExtent(options.cols, options.rows, options.view3D?.depthPercent);
91799
- const floorSize = Math.max(extent.gridWidth, extent.gridDepth) * 1.2;
91800
- const gridSegs = Math.max(options.cols, options.rows);
91801
- const gridFloor = new three.GridHelper(floorSize, gridSegs, 0xcccccc, 0xe8e8e8);
91802
- gridFloor.position.y = -0.02;
91803
- scene.add(gridFloor);
91804
- const walls = options.wallColors
91805
- ? buildSurfaceWallMeshes(three, options.cols, options.rows, MAX_VALUE_HEIGHT, options.wallColors, extent)
91806
- : null;
91807
- for (const mesh of walls?.meshes ?? []) {
91808
- scene.add(mesh);
91809
- }
91810
- const markerGeometry = new three.SphereGeometry(MARKER_RADIUS, 8, 6);
91811
- const tubeGeometries = [];
91812
- const ribbonGeometries = [];
91813
- const otherMaterials = [];
91814
- const markerMeshes = [];
91815
- const markerMaterials = [];
91816
- for (const path of options.series) {
91817
- const ribbonGeometry = buildAreaRibbonGeometry(three, path);
91818
- if (ribbonGeometry) {
91819
- const ribbonMaterial = new three.MeshPhongMaterial({
91820
- color: path.color,
91821
- side: three.DoubleSide,
91822
- transparent: true,
91823
- opacity: RIBBON_OPACITY,
91824
- shininess: 10,
91825
- });
91826
- scene.add(new three.Mesh(ribbonGeometry, ribbonMaterial));
91827
- ribbonGeometries.push(ribbonGeometry);
91828
- otherMaterials.push(ribbonMaterial);
91829
- }
91830
- if (path.vertices.length >= 2) {
91831
- const curve = new three.CatmullRomCurve3(path.vertices.map((v) => new three.Vector3(...v.position)));
91832
- const tubeGeometry = new three.TubeGeometry(curve, Math.max(path.vertices.length * 8, 16), TUBE_RADIUS, 8, false);
91833
- const tubeMaterial = new three.MeshPhongMaterial({ color: path.color, shininess: 30 });
91834
- scene.add(new three.Mesh(tubeGeometry, tubeMaterial));
91835
- tubeGeometries.push(tubeGeometry);
91836
- otherMaterials.push(tubeMaterial);
91837
- }
91838
- for (const v of path.vertices) {
91839
- const markerMaterial = new three.MeshPhongMaterial({ color: path.color, shininess: 30 });
91840
- const marker = new three.Mesh(markerGeometry, markerMaterial);
91841
- marker.position.set(...v.position);
91842
- marker.userData = {
91843
- seriesIndex: v.seriesIndex,
91844
- categoryIndex: v.categoryIndex,
91845
- value: v.value,
91846
- };
91847
- scene.add(marker);
91848
- markerMeshes.push(marker);
91849
- markerMaterials.push(markerMaterial);
91850
- otherMaterials.push(markerMaterial);
91851
- }
91852
- }
91853
- const tooltipData = {
91854
- categoryLabels: options.categoryLabels,
91855
- seriesNames: options.seriesNames,
91856
- numberFormats: options.numberFormats,
91857
- };
91858
- const hoverTooltip = attachChart3DHoverTooltip({
91859
- three,
91860
- canvas,
91861
- camera,
91862
- meshes: markerMeshes,
91863
- buildTooltip: (intersection) => buildCartesianChart3DHoverTooltip(intersection?.object.userData, tooltipData),
91864
- });
91865
- // Click-to-select + drag-to-value (see cartesian-chart-3d-interaction-wiring.ts).
91866
- const pointerInteraction = attachCartesianChart3DInteraction({
91867
- three,
91868
- canvas,
91869
- camera,
91870
- controls,
91871
- width,
91872
- height,
91873
- markerMeshes,
91874
- markerMaterials: markerMaterials,
91875
- series: options.series,
91876
- interaction,
91877
- });
91878
- const labels = buildCartesianChart3DLabels(options.cols, options.rows, options.categoryLabels, options.seriesNames, options.view3D?.depthPercent);
91879
- const labelProjector = createChart3DLabelProjector(three, ctx.document, labels);
91880
- labelProjector.applyTextStyle(options.textStyle);
91881
- ctx.overlay.appendChild(labelProjector.layer);
91882
- return finishHostedChart3DScene(ctx, stage, {
91883
- afterRender: (cam, w, h) => labelProjector.update(cam, w, h),
91884
- resize: (w, h) => pointerInteraction.updateSize(w, h),
91885
- setSelectedPart: (part) => pointerInteraction.setSelectedPart(part),
91886
- setTextStyle: (style) => labelProjector.applyTextStyle(style),
91887
- dispose() {
91888
- hoverTooltip.dispose();
91889
- pointerInteraction.dispose();
91890
- markerGeometry.dispose();
91891
- for (const g of [...tubeGeometries, ...ribbonGeometries]) {
91892
- g.dispose();
91893
- }
91894
- for (const m of otherMaterials) {
91895
- m.dispose();
91896
- }
91897
- gridFloor.dispose();
91898
- walls?.dispose();
91899
- labelProjector.layer.remove();
91900
- },
91901
- }, interaction);
91902
- }
91903
-
91904
- /**
91905
- * Pure raycast-hit -> hover-tooltip mapping for the interactive 3D pie chart
91906
- * scene ({@link ./pie-chart-3d-scene.ts}).
91907
- *
91908
- * Like `bar3D` (each data point is its own box mesh), every pie3D wedge is
91909
- * its OWN `CylinderGeometry` mesh, so the scene controller tags every wedge's
91910
- * `userData` with its point index directly and a `Raycaster.intersectObjects`
91911
- * hit reports it straight from the hit object - no face-index arithmetic
91912
- * needed. This module only builds the tooltip text from that index, kept
91913
- * three-agnostic so it is unit-testable without mocking WebGL. Mirrors
91914
- * {@link ./bar-chart-3d-hit-test.ts} exactly.
91915
- *
91916
- * @module pie-chart-3d-hit-test
91917
- */
91918
- /**
91919
- * Build the hover-tooltip text for a raycast hit on a pie3D wedge mesh, or
91920
- * `undefined` when there is no hit.
91921
- *
91922
- * Mirrors `buildMarkTooltip`'s "<series>, <category>: <value>" text exactly,
91923
- * so a pie3D wedge's tooltip reads identically to the flat SVG pie
91924
- * renderer's.
91925
- */
91926
- function buildPieChart3DHoverTooltip(hit, data) {
91927
- if (!hit) {
91928
- return undefined;
91929
- }
91930
- return buildMarkTooltip(data.seriesName, data.categoryLabels[hit.pointIndex], hit.value, data.numberFormat);
91931
- }
91932
-
91933
- /**
91934
- * Drag-to-value math for an interactive 3D `pie3D` wedge, three-agnostic so it
91935
- * is unit-testable without mocking WebGL.
91936
- *
91937
- * A pie3D wedge has no single visible world axis a screen-space pointer delta
91938
- * can be projected onto the way a bar3D box's height or a surface3D vertex's
91939
- * height can (`chart-3d-pointer-interaction.ts`'s `Chart3DDragCalibrationInput`
91940
- * model): like the flat SVG pie/doughnut, a wedge's value is a SHARE of the
91941
- * series total, changed by sweeping its trailing edge around the pie's centre.
91942
- * `chart-interaction-pie.ts`'s `resolvePieSliceShareValue` already holds that
91943
- * exact renormalisation formula (dragging the flat pie), so this module only
91944
- * supplies the 3D-specific half: turning a raycast hit on the pie's own plane
91945
- * into an angle in the SAME convention `computePieChart3DSliceAngles` uses for
91946
- * `startAngle`/`thetaStart`.
91947
- *
91948
- * `THREE.CylinderGeometry` places a vertex at parametric angle `theta` at
91949
- * local `(x, z) = (radius*sin(theta), radius*cos(theta))` (its own `generateTorso`
91950
- * step), so recovering `theta` from a world `(x, z)` hit point uses
91951
- * `Math.atan2(x, z)`, NOT the `atan2(y, x)` convention the flat 2D SVG pie's
91952
- * screen-space drag uses.
91953
- *
91954
- * @module pie-chart-3d-drag
91955
- */
91956
- /**
91957
- * Resolve the drag geometry for the wedge at `pointIndex`, or `null` when it
91958
- * cannot be found in `wedges` or `pointIndex` is out of range in `values`.
91959
- */
91960
- function buildPieChart3DDragGeometry(wedges, values, pointIndex) {
91961
- if (pointIndex < 0 || pointIndex >= values.length) {
91962
- return null;
91963
- }
91964
- const wedge = wedges.find((w) => w.pointIndex === pointIndex);
91965
- if (!wedge) {
91966
- return null;
91967
- }
91968
- return { values, pointIndex, leadingAngle: wedge.startAngle };
91969
- }
91970
- /**
91971
- * Convert a world-space hit point on the pie's own plane (already relative to
91972
- * the pie's centre, i.e. the `x`/`z` of a raycast against the invisible disc
91973
- * `pie-chart-3d-interaction-wiring.ts` raycasts, not a wedge mesh's own,
91974
- * possibly `c:explosion`-offset, local space) into the angle convention
91975
- * `computePieChart3DSliceAngles`'s `startAngle`/`thetaStart` use.
91976
- */
91977
- function pieChart3DPointerAngle(x, z) {
91978
- return Math.atan2(x, z);
91979
- }
91980
- /**
91981
- * New value for the dragged wedge given the pointer's current angle on the
91982
- * pie's plane (see {@link pieChart3DPointerAngle}). Delegates the actual
91983
- * renormalisation to the flat 2D pie's own `resolvePieSliceShareValue`, so a
91984
- * pie3D wedge drag renormalises every other (unchanged) slice's value exactly
91985
- * like its flat SVG counterpart.
91986
- */
91987
- function resolvePieChart3DDragValue(geometry, pointerAngle) {
91988
- return resolvePieSliceShareValue(pointerAngle, geometry.leadingAngle, geometry.values, geometry.pointIndex);
91989
- }
91990
-
91991
- /**
91992
- * Attach click-to-select / drag-to-value handling to a mounted pie3D scene.
91993
- * Unlike {@link ../chart-3d-pointer-interaction.ts}'s generic
91994
- * `attachChartPointerInteraction`, this raycasts the wedge meshes only to
91995
- * decide WHICH wedge a press landed on; the drag itself samples an invisible
91996
- * disc (the pie's own XZ plane through its centre) on every pointer move.
91997
- */
91998
- function attachPieChart3DInteraction(params) {
91999
- const raycaster = new params.three.Raycaster();
92000
- const ndc = new params.three.Vector2();
92001
- // The pie's own plane: normal +Y, through the world origin (every wedge's
92002
- // unexploded local origin, per `computePieChart3DSliceAngles`).
92003
- const plane = new params.three.Plane(new params.three.Vector3(0, 1, 0), 0);
92004
- const planeHit = new params.three.Vector3();
92005
- let dragState = null;
92006
- const highlightEntries = params.wedgeMeshes.map((mesh, i) => {
92007
- const hit = mesh.userData;
92008
- return {
92009
- mark: { seriesIndex: 0, pointIndex: hit.pointIndex },
92010
- material: params.wedgeMaterials[i],
92011
- };
92012
- });
92013
- function setNdc(clientX, clientY) {
92014
- const rect = params.canvas.getBoundingClientRect();
92015
- if (rect.width <= 0 || rect.height <= 0) {
92016
- return false;
92017
- }
92018
- ndc.x = ((clientX - rect.left) / rect.width) * 2 - 1;
92019
- ndc.y = -((clientY - rect.top) / rect.height) * 2 + 1;
92020
- return true;
92021
- }
92022
- function raycastWedge(clientX, clientY) {
92023
- if (!setNdc(clientX, clientY)) {
92024
- return undefined;
92025
- }
92026
- raycaster.setFromCamera(ndc, params.camera);
92027
- return raycaster.intersectObjects(params.wedgeMeshes, false)[0];
92028
- }
92029
- /** The pointer's current angle on the pie's plane, or `null` when the ray is (near) parallel to it. */
92030
- function planeAngleAt(clientX, clientY) {
92031
- if (!setNdc(clientX, clientY)) {
92032
- return null;
92033
- }
92034
- raycaster.setFromCamera(ndc, params.camera);
92035
- const hit = raycaster.ray.intersectPlane(plane, planeHit);
92036
- return hit ? pieChart3DPointerAngle(hit.x, hit.z) : null;
92037
- }
92038
- function setSelectedPart(part) {
92039
- applyChart3DMeshHighlight(highlightEntries, part);
92040
- }
92041
- function travelBelowThreshold(clientX, clientY, state) {
92042
- const dx = clientX - state.startClientX;
92043
- const dy = clientY - state.startClientY;
92044
- return Math.hypot(dx, dy) < CHART_3D_DRAG_THRESHOLD_PX;
92045
- }
92046
- const onPointerDown = (event) => {
92047
- params.canvas.setPointerCapture?.(event.pointerId);
92048
- const hit = raycastWedge(event.clientX, event.clientY);
92049
- const hitData = hit?.object.userData;
92050
- const part = chart3DHitToPartRef(hitData ? { seriesIndex: 0, pointIndex: hitData.pointIndex } : null);
92051
- // Same armed gate as `chart-3d-pointer-interaction.ts` (see its module
92052
- // doc): a wedge press on an armed chart is the scene's own select /
92053
- // value-drag, never the stage's element move; on an un-armed chart it
92054
- // bubbles so the first click still selects the chart.
92055
- const armed = part !== null && isChartInteractionArmed(params.canvas);
92056
- if (armed) {
92057
- event.stopPropagation();
92058
- }
92059
- let geometry = null;
92060
- if (part && hitData && armed) {
92061
- const wedges = params.getWedges();
92062
- const wedge = wedges.find((w) => w.pointIndex === hitData.pointIndex);
92063
- if (wedge) {
92064
- geometry = {
92065
- values: wedges.map((w) => w.value),
92066
- pointIndex: hitData.pointIndex,
92067
- leadingAngle: wedge.startAngle,
92068
- };
92069
- }
92070
- }
92071
- dragState = {
92072
- part,
92073
- geometry,
92074
- moved: false,
92075
- startClientX: event.clientX,
92076
- startClientY: event.clientY,
92077
- lastAngle: null,
92078
- };
92079
- if (geometry) {
92080
- // Cancel the press too: see `chart-3d-pointer-interaction.ts` (the
92081
- // compatibility mousedown drives the React stage's desktop move).
92082
- event.preventDefault();
92083
- params.controls.enabled = false;
92084
- }
92085
- };
92086
- const onPointerMove = (event) => {
92087
- const state = dragState;
92088
- if (!state || !state.part || !state.geometry) {
92089
- return;
92090
- }
92091
- if (!state.moved && travelBelowThreshold(event.clientX, event.clientY, state)) {
92092
- return;
92093
- }
92094
- state.moved = true;
92095
- const angle = planeAngleAt(event.clientX, event.clientY);
92096
- if (angle === null) {
92097
- return;
92098
- }
92099
- state.lastAngle = angle;
92100
- const value = resolvePieChart3DDragValue(state.geometry, angle);
92101
- params.interaction?.onValueDragPreview?.(state.part, value);
92102
- };
92103
- const onPointerUp = (event) => {
92104
- if (!dragState) {
92105
- return;
92106
- }
92107
- const state = dragState;
92108
- dragState = null;
92109
- params.canvas.releasePointerCapture?.(event.pointerId);
92110
- if (state.geometry) {
92111
- params.controls.enabled = true;
92112
- }
92113
- if (state.moved && state.part && state.geometry) {
92114
- const angle = planeAngleAt(event.clientX, event.clientY) ?? state.lastAngle;
92115
- if (angle !== null) {
92116
- const value = resolvePieChart3DDragValue(state.geometry, angle);
92117
- params.interaction?.onValueDragCommit?.(state.part, value);
92118
- }
92119
- return;
92120
- }
92121
- setSelectedPart(state.part);
92122
- params.interaction?.onSelect?.(state.part);
92123
- };
92124
- params.canvas.addEventListener('pointerdown', onPointerDown);
92125
- params.canvas.addEventListener('pointermove', onPointerMove);
92126
- params.canvas.addEventListener('pointerup', onPointerUp);
92127
- params.canvas.addEventListener('pointercancel', onPointerUp);
92128
- return {
92129
- setSelectedPart,
92130
- dispose() {
92131
- params.canvas.removeEventListener('pointerdown', onPointerDown);
92132
- params.canvas.removeEventListener('pointermove', onPointerMove);
92133
- params.canvas.removeEventListener('pointerup', onPointerUp);
92134
- params.canvas.removeEventListener('pointercancel', onPointerUp);
92135
- },
92136
- };
92137
- }
92138
-
92139
- /** Minimum radial segments per wedge so a thin slice is still visibly curved. */
92140
- const MIN_RADIAL_SEGMENTS = 3;
92141
- /** Radial segments for a full-circle wedge (360deg); scaled down for narrower slices. */
92142
- const FULL_CIRCLE_SEGMENTS = 64;
92143
- function wedgeSegments(thetaLength) {
92144
- return Math.max(MIN_RADIAL_SEGMENTS, Math.round((thetaLength / (Math.PI * 2)) * FULL_CIRCLE_SEGMENTS));
92145
- }
92146
- /**
92147
- * Build one `THREE.CylinderGeometry` wedge mesh per data point: radiusTop =
92148
- * radiusBottom = `outerRadius`, height = `thickness`, capped (`openEnded`
92149
- * `false`) so a partial `thetaLength` gets real flat top/bottom + curved rim +
92150
- * flat radial "cut" faces for free. Adds every mesh to `scene`.
92151
- */
92152
- function buildPieChart3DWedgeMeshes(three, scene, wedges, outerRadius, thickness) {
92153
- const meshes = [];
92154
- const geometries = [];
92155
- const materials = [];
92156
- for (const wedge of wedges) {
92157
- const geometry = new three.CylinderGeometry(outerRadius, outerRadius, thickness, wedgeSegments(wedge.thetaLength), 1, false, wedge.startAngle, wedge.thetaLength);
92158
- const material = new three.MeshPhongMaterial({ color: wedge.color, shininess: 30 });
92159
- const mesh = new three.Mesh(geometry, material);
92160
- mesh.position.set(wedge.explodeOffset[0], 0, wedge.explodeOffset[1]);
92161
- mesh.userData = { pointIndex: wedge.pointIndex, value: wedge.value };
92162
- scene.add(mesh);
92163
- meshes.push(mesh);
92164
- geometries.push(geometry);
92165
- materials.push(material);
92166
- }
92167
- return { meshes, geometries, materials };
92168
- }
92169
- /**
92170
- * Rebuild every wedge mesh's geometry + position + `userData` from a freshly
92171
- * recomputed angle set (a live value drag: `pie-chart-3d-scene.ts`'s
92172
- * `recomputeLiveAngles`), mutating `geometries` in place (disposing each old
92173
- * one) so `createPieChart3DScene`'s own `dispose()` still tears down exactly the
92174
- * geometries actually on screen, whichever wedge was last dragged.
92175
- */
92176
- function applyPieChart3DWedgeAngles(three, meshes, geometries, outerRadius, thickness, angles) {
92177
- for (const angle of angles) {
92178
- const mesh = meshes[angle.pointIndex];
92179
- const oldGeometry = geometries[angle.pointIndex];
92180
- if (!mesh || !oldGeometry) {
92181
- continue;
92182
- }
92183
- const geometry = new three.CylinderGeometry(outerRadius, outerRadius, thickness, wedgeSegments(angle.thetaLength), 1, false, angle.startAngle, angle.thetaLength);
92184
- mesh.geometry = geometry;
92185
- oldGeometry.dispose();
92186
- geometries[angle.pointIndex] = geometry;
92187
- mesh.position.set(angle.explodeOffset[0], 0, angle.explodeOffset[1]);
92188
- mesh.userData = { pointIndex: angle.pointIndex, value: angle.value };
92189
- }
92190
- }
92191
-
92192
- /**
92193
- * three.js 3D pie-chart scene for `<pptx-three-view>` (framework-agnostic).
92194
- *
92195
- * Builds an interactive `pie3D` chart as a `<pptx-three-view>` scene (drawn
92196
- * by the shared renderer; see {@link ./chart-3d-hosted-stage.ts}): builds
92197
- * one real `THREE.CylinderGeometry` wedge mesh per data point (a partial-arc
92198
- * cylinder, so each wedge gets a flat top/bottom face, the two curved rim
92199
- * faces, and the two flat radial "cut" faces for free), lights, a perspective
92200
- * camera driven by `c:view3D` (`rotX`/`rotY`/`rperspective`/`hPercent`) and
92201
- * OrbitControls. Raycasts pointer moves against the wedges to
92202
- * set a native hover tooltip on the canvas element (see
92203
- * {@link ./pie-chart-3d-hit-test.ts}), matching every other chart kind's
92204
- * SVG-`<title>` hover tooltip.
92205
- *
92206
- * Unlike the cartesian 3D scenes (bar3D/line3D/area3D), a pie has no plot
92207
- * rectangle to wall in, so this module mounts no grid floor or
92208
- * `c:floor`/`c:sideWall`/`c:backWall` panels, matching PowerPoint's own
92209
- * behaviour and the flat SVG engine's `chart-3d-surfaces.ts` doc comment.
92210
- *
92211
- * A wedge is also drag-to-value, exactly like the flat SVG pie/doughnut:
92212
- * dragging sweeps its trailing edge around the pie's centre, and every other
92213
- * slice's ANGLE renormalises live (its own absolute value stays fixed, but the
92214
- * series total the pie's angles divide against changes) - see
92215
- * `pie-chart-3d-drag.ts` / `pie-chart-3d-interaction-wiring.ts`. Because the
92216
- * bar3D/line3D/area3D/surface3D drag calibration in
92217
- * `chart-3d-pointer-interaction.ts` only handles a single fixed WORLD axis,
92218
- * this scene wires its own pointer interaction instead of that shared one.
92219
- *
92220
- * @module pie-chart-3d-scene
92221
- */
92222
- /** Build the hosted pie3D scene. */
92223
- function createPieChart3DScene(ctx, options) {
92224
- const placement = computePieChart3DCameraPlacement(options.view3D);
92225
- const stage = createHostedChart3DStage(ctx, placement, { minDistance: 0.5, maxDistance: 30 }, 0.65);
92226
- const { three, scene, camera, canvas, controls } = stage;
92227
- const interaction = hostedChart3DInteraction(ctx);
92228
- // One CylinderGeometry mesh per wedge (see pie-chart-3d-mesh.ts for why a
92229
- // partial-arc capped cylinder gets a full wedge shape for free).
92230
- const { meshes: wedgeMeshes, geometries: wedgeGeometries, materials: wedgeMaterials, } = buildPieChart3DWedgeMeshes(three, scene, options.wedges, options.outerRadius, options.thickness);
92231
- // Raycast-based hover tooltip: each wedge mesh carries its own point index
92232
- // in `userData`, so a hit reports the slice directly (no face-index -> cell
92233
- // arithmetic, matching bar3D's box meshes).
92234
- const hoverTooltip = attachChart3DHoverTooltip({
92235
- three,
92236
- canvas,
92237
- camera,
92238
- meshes: wedgeMeshes,
92239
- buildTooltip: (intersection) => buildPieChart3DHoverTooltip(intersection?.object.userData, {
92240
- categoryLabels: options.categoryLabels,
92241
- seriesName: options.seriesName,
92242
- numberFormat: options.numberFormat,
92243
- }),
92244
- });
92245
- // Live wedge-angle snapshot, updated by `recomputeLiveAngles` on every value
92246
- // drag preview/commit tick so `getWedges` (re-read by the interaction
92247
- // wiring at the start of every press) always reflects the on-screen
92248
- // geometry, even across several drags within one mount, before any commit
92249
- // round-trips through the caller and remounts the whole scene fresh.
92250
- let currentAngles = options.wedges;
92251
- let liveValues = options.values.slice();
92252
- /** Recompute every wedge's angle from `liveValues` (one value already replaced by the caller) and rebuild its mesh. */
92253
- function recomputeLiveAngles() {
92254
- const angles = computePieChart3DSliceAngles(liveValues, options.explosions, options.firstSliceAngleDeg, options.outerRadius);
92255
- currentAngles = angles;
92256
- applyPieChart3DWedgeAngles(three, wedgeMeshes, wedgeGeometries, options.outerRadius, options.thickness, angles);
92257
- }
92258
- // Click-to-select + drag-to-value: each wedge is its own mesh, so it gets
92259
- // the same emissive highlight bar3D/line3D/area3D marks do, and the ANGLE
92260
- // drag (see the module doc comment) is wired through
92261
- // `pie-chart-3d-interaction-wiring.ts` rather than the generic
92262
- // single-fixed-axis calibration `chart-3d-pointer-interaction.ts` uses for
92263
- // bar3D/line3D/area3D/surface3D.
92264
- const pointerInteraction = attachPieChart3DInteraction({
92265
- three,
92266
- canvas,
92267
- camera,
92268
- controls,
92269
- wedgeMeshes,
92270
- wedgeMaterials: wedgeMaterials,
92271
- getWedges: () => currentAngles,
92272
- interaction: {
92273
- onSelect: interaction?.onSelect,
92274
- onValueDragPreview: (part, value) => {
92275
- if (part.pointIndex !== undefined) {
92276
- liveValues = liveValues.map((v, i) => (i === part.pointIndex ? value : v));
92277
- recomputeLiveAngles();
92278
- }
92279
- interaction?.onValueDragPreview?.(part, value);
92280
- },
92281
- onValueDragCommit: (part, value) => {
92282
- if (part.pointIndex !== undefined) {
92283
- liveValues = liveValues.map((v, i) => (i === part.pointIndex ? value : v));
92284
- recomputeLiveAngles();
92285
- }
92286
- interaction?.onValueDragCommit?.(part, value);
92287
- },
92288
- },
92289
- });
92290
- return finishHostedChart3DScene(ctx, stage, {
92291
- // No `resize`: this scene's own wiring re-derives the pointer's NDC
92292
- // position from `canvas.getBoundingClientRect()` on every raycast.
92293
- setSelectedPart: (part) => pointerInteraction.setSelectedPart(part),
92294
- dispose() {
92295
- hoverTooltip.dispose();
92296
- pointerInteraction.dispose();
92297
- for (const g of wedgeGeometries) {
92298
- g.dispose();
92299
- }
92300
- for (const m of wedgeMaterials) {
92301
- m.dispose();
92302
- }
92303
- },
92304
- }, interaction);
92305
- }
92306
-
92307
89339
  /**
92308
89340
  * SmartArt fallback-layout paint decisions: where a node's label sits and how a
92309
89341
  * connector is stroked.
@@ -120270,16 +117302,6 @@ async function loadChart3DThree() {
120270
117302
  return null;
120271
117303
  }
120272
117304
  }
120273
- /** Dynamically load the OrbitControls addon; resolves to `null` when unavailable. */
120274
- async function loadChart3DOrbitControls() {
120275
- try {
120276
- const mod = await import('three/examples/jsm/controls/OrbitControls.js');
120277
- return mod.OrbitControls;
120278
- }
120279
- catch {
120280
- return null;
120281
- }
120282
- }
120283
117305
 
120284
117306
  /** Largest backing-store edge a single view is drawn at, in device pixels. */
120285
117307
  const MAX_VIEW_PIXELS = 4096;
@@ -120461,8 +117483,8 @@ function resetThreeRendererHostForTests() {
120461
117483
  }
120462
117484
 
120463
117485
  const loaders = {
120464
- chart: async () => (await import('./pptx-angular-viewer-chart-3d-view-scene-CreE1jB0.mjs')).mountChart3DView,
120465
- smartart: async () => (await import('./pptx-angular-viewer-view-scene-DADbVF7v.mjs')).mountSmartArt3DView,
117486
+ chart: async () => (await import('./pptx-angular-viewer-chart-3d-view-scene-Dv-HeL_n.mjs')).mountChart3DView,
117487
+ smartart: async () => (await import('./pptx-angular-viewer-view-scene-HXKpEkAE.mjs')).mountSmartArt3DView,
120466
117488
  };
120467
117489
  /** Resolve the scene factory for a spec kind. */
120468
117490
  async function loadThreeViewScene(kind) {
@@ -120624,10 +117646,6 @@ class ThreeViewController {
120624
117646
  let view = null;
120625
117647
  const scene = await factory(spec.spec, {
120626
117648
  three,
120627
- // No camera orbit: PowerPoint never rotates a chart or SmartArt
120628
- // under the pointer, and an orbit would turn the scene while the
120629
- // user drags the element to move it on the slide.
120630
- OrbitControls: null,
120631
117649
  size: this.size,
120632
117650
  eventTarget: this.opts.eventTarget,
120633
117651
  overlay,
@@ -130641,7 +127659,7 @@ function createLocalStorageBackend(namespace) {
130641
127659
  /** Try IndexedDB first; fall back to localStorage on any failure. */
130642
127660
  async function resolveBackend(dbName, namespace) {
130643
127661
  try {
130644
- const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-D8QI7rBh.mjs');
127662
+ const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-A_FqG3yo.mjs');
130645
127663
  const db = await openChatDb(dbName);
130646
127664
  return createIdbBackend(db);
130647
127665
  }
@@ -148249,20 +145267,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
148249
145267
  function getSmartArtData(element) {
148250
145268
  return element.type === 'smartArt' ? element.smartArtData : undefined;
148251
145269
  }
148252
- /**
148253
- * Locate the topmost element bearing `data-smartart-node-id` in a
148254
- * `document.elementsFromPoint` result (which, unlike a plain hit-test,
148255
- * includes elements with `pointer-events: none` such as the invisible
148256
- * overlay's SVG nodes), or `null` when none does.
148257
- */
148258
- function findSmartArtNodeElementAtPoint(elements) {
148259
- for (const el of elements) {
148260
- if (el.hasAttribute('data-smartart-node-id')) {
148261
- return el;
148262
- }
148263
- }
148264
- return null;
148265
- }
148266
145270
  /** Position a node's bounding rect relative to its container, for the overlaid textarea. */
148267
145271
  function computeNode3DEditBox(nodeRect, containerRect) {
148268
145272
  return {
@@ -148366,22 +145370,26 @@ class SmartArt3DRendererComponent {
148366
145370
  });
148367
145371
  }
148368
145372
  /**
148369
- * Locate the SmartArt node at the click position using `elementsFromPoint`
148370
- * (which includes pointer-events:none SVG elements) and open the inline editor.
145373
+ * Locate the SmartArt node at the click position within the invisible
145374
+ * overlay and open the inline editor. The overlay's node groups are
145375
+ * `pointer-events: none`, which hit-testing (`document.elementsFromPoint`
145376
+ * included) skips, so the node is found by geometry
145377
+ * (`smartArtNodeAtPoint`).
148371
145378
  */
148372
145379
  onOverlayDblClick(event) {
148373
145380
  const container = this.containerEl()?.nativeElement;
148374
145381
  const data = this.smartArtData();
148375
- if (!container || !data) {
145382
+ const overlay = event.currentTarget;
145383
+ if (!container || !data || !(overlay instanceof Element)) {
148376
145384
  return;
148377
145385
  }
148378
- // elementsFromPoint includes pointer-events:none nodes, so this finds the
148379
- // <g data-smartart-node-id="..."> in the invisible overlay SVG.
148380
- const nodeEl = findSmartArtNodeElementAtPoint(document.elementsFromPoint(event.clientX, event.clientY));
145386
+ const nodeEl = smartArtNodeAtPoint(overlay, event.clientX, event.clientY);
148381
145387
  const nodeId = nodeEl?.getAttribute('data-smartart-node-id');
148382
145388
  if (!nodeEl || !nodeId) {
148383
145389
  return;
148384
145390
  }
145391
+ // The editor owns this double-click: the stage must not act on it too.
145392
+ event.stopPropagation();
148385
145393
  const currentText = data.nodes.find((n) => n.id === nodeId)?.text ?? '';
148386
145394
  this.draftText = currentText;
148387
145395
  this.editSettled = false;
@@ -176636,7 +173644,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
176636
173644
  }], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }] } });
176637
173645
 
176638
173646
  // Generated by scripts/inline-shared.mjs from package.json. Do not edit.
176639
- const PPTX_ANGULAR_VIEWER_VERSION = "4.10.0";
173647
+ const PPTX_ANGULAR_VIEWER_VERSION = "4.11.0";
176640
173648
 
176641
173649
  /**
176642
173650
  * account-page.component.ts: File > Account content.
@@ -214965,5 +211973,5 @@ function cn(...values) {
214965
211973
  * Generated bundle index. Do not edit.
214966
211974
  */
214967
211975
 
214968
- export { AiFocusBarComponent as $, smartArt3DGradientCoordinate as A, threeViewOverflowFromNdc as B, CHART_3D_DRAG_THRESHOLD_PX as C, DEFAULT_PALETTE$1 as D, hasThreeViewOverflow as E, overflowViewOffset as F, smartArt3DCameraMatrix as G, smartArt3DEyeInDiagram as H, resolveSmartArt3DLightModel as I, AFTER_ANIMATION_VALUES as J, ALIGN_OPTIONS as K, ANIMATION_PRESET_CATEGORIES as L, AUDIENCE_HASH as M, NO_THREE_VIEW_OVERFLOW as N, AUDIENCE_NONCE_KEY as O, AVATAR_COLOR_SWATCHES as P, AXIS_LABEL_COLOR as Q, AccessibilityPanelComponent as R, AccessibilityService as S, AccessibilityTextPanelComponent as T, AccountPageComponent as U, ActionSettingsPanelComponent as V, AdvancedChartEditorComponent as W, AiChangeOverlayComponent as X, AiChatPanelComponent as Y, AiChatService as Z, AiComposerComponent as _, appendChartLegendSvg as a, DIRECTIONAL_PRESETS as a$, AiFocusHighlightOverlayComponent as a0, AiHistoryMenuComponent as a1, AiHistoryService as a2, AiMessageListComponent as a3, AiPanelStore as a4, AiProposalCardComponent as a5, AiSettingsSectionComponent as a6, AiToolCallCardComponent as a7, AnimationAuthorPanelComponent as a8, AnimationPanelComponent as a9, ChartTypeSelectorComponent as aA, CollaborationCursorsComponent as aB, CollaborationService as aC, ColorChangedImageComponent as aD, CommentMarkersOverlayComponent as aE, CommentsPanelComponent as aF, CommentsService as aG, ComparePanelComponent as aH, ConnectorRendererComponent as aI, ConnectorTextOverlayComponent as aJ, CustomShowsComponent as aK, DEFAULT_BOUNDS as aL, DEFAULT_BROADCAST_SERVER_URL as aM, DEFAULT_CANVAS_HEIGHT as aN, DEFAULT_CANVAS_WIDTH as aO, DEFAULT_COLOR_SCHEME as aP, DEFAULT_FILL_COLOR$1 as aQ, DEFAULT_LAYOUT as aR, DEFAULT_MORPH_DURATION_MS as aS, DEFAULT_PATTERN_FILL_PRESET as aT, DEFAULT_PRINT_SETTINGS as aU, DEFAULT_SLIDE_BACKGROUND as aV, DEFAULT_STROKE_COLOR as aW, DEFAULT_STYLE as aX, DEFAULT_TABLE_ROW_HEIGHT as aY, DEFAULT_TEXT_COLOR$2 as aZ, DEFAULT_VIEWER_PROFILE as a_, AnimationPlaybackService as aa, AutosaveRecoveryDialogComponent as ab, AutosaveService as ac, BACKSTAGE_CARD_IDS as ad, BACKSTAGE_PAGE_IDS as ae, BroadcastDialogComponent as af, CANVAS_CONTEXT_MENU_COMMAND_IDS as ag, CHART_EDITOR_STYLES as ah, CINEMATIC_TRANSITION_KEYFRAMES as ai, CURSOR_PALETTE as aj, CanvasFitService as ak, ChartAxisOptionsComponent as al, ChartAxisStyleOptionsComponent as am, ChartComboTypeOptionsComponent as an, ChartDataEditorComponent as ao, ChartDataLabelOptionsComponent as ap, ChartDatapointMarkerOptionsComponent as aq, ChartDatapointOptionsComponent as ar, ChartDisplayOptionsComponent as as, ChartElementViewComponent as at, ChartErrorBarOptionsComponent as au, ChartMarkerOptionsComponent as av, ChartPartSelectionService as aw, ChartPrimitivesComponent as ax, ChartRendererComponent as ay, ChartTrendlineOptionsComponent as az, renderChartTextSvg as b, MobileSlidesSheetComponent as b$, DIRECTION_OPTIONS as b0, DocumentPropertiesCardComponent as b1, EASE as b2, EDITOR_SHORTCUT_ACTION_IDS as b3, EFFECT_SOUND_CATALOGUE as b4, ELEMENT_CONTEXT_MENU_COMMAND_IDS as b5, EMBEDDED_FONTS_STYLE_ID as b6, EMPHASIS_PRESETS as b7, ENTRANCE_PRESETS as b8, TEMPLATES as b9, HeaderFooterDialogComponent as bA, HyperlinkDialogComponent as bB, INSTANT as bC, ImagePropertiesPanelComponent as bD, InkDrawingService as bE, InkRendererComponent as bF, InsertSmartArtDialogComponent as bG, InspectorPaneHeaderComponent as bH, InspectorPanelComponent as bI, IsMobileService as bJ, KeepAnnotationsDialogComponent as bK, LOCALE_CATALOG as bL, LONG_PRESS_DURATION_MS as bM, LONG_PRESS_MOVE_TOLERANCE_PX as bN, LoadContentService as bO, LocalPresencePublisher as bP, MAX_ZOOM_SCALE as bQ, MIN_ZOOM_SCALE as bR, MOTION_PATH_COLUMNS as bS, MediaPreviewComponent as bT, MediaPropertiesPanelComponent as bU, MediaRendererComponent as bV, MediaTrimTimelineComponent as bW, MobileBottomBarComponent as bX, MobileMenuSheetComponent as bY, MobilePresenterViewComponent as bZ, MobileSheetComponent as b_, EXIT_PRESETS as ba, EditorContextMenuComponent as bb, EditorHistory as bc, EditorStateService as bd, EditorToolbarComponent as be, EffectsPanelComponent as bf, ElementRendererComponent as bg, EmbeddedFontsService as bh, EncryptedFileDialogComponent as bi, EquationEditorDialogComponent as bj, EquationRendererComponent as bk, EquationTemplateGalleryComponent as bl, ExportProgressModalComponent as bm, ExportService as bn, FieldContextService as bo, FindBarComponent as bp, FindReplaceBarComponent as bq, FollowModeBarComponent as br, FontEmbeddingListComponent as bs, FontEmbeddingPanelComponent as bt, GALLERY_THEME_PRESETS as bu, GOOGLE_WEBFONTS_LINK_ID as bv, GRIDLINE_COLOR$1 as bw, GoogleWebfontsService as bx, GradientPickerComponent as by, HANDOUT_OPTIONS as bz, createSvgEl as c, RibbonInsertSectionComponent as c$, MobileToolbarComponent as c0, ModalDialogComponent as c1, Model3DRendererComponent as c2, NotesHandoutCardComponent as c3, NotesPanelComponent as c4, NotesToolbarComponent as c5, OPTIONS_PAGE_IDS as c6, OPTIONS_SECTION_IDS as c7, OPTIONS_SETTING_IDS as c8, OleRendererComponent as c9, PrintDialogComponent as cA, PrintService as cB, PrintSettingsPanelComponent as cC, PropertiesDialogComponent as cD, RANDOM_ELIGIBLE_TYPES as cE, REPEAT_MODE_OPTIONS as cF, RESIZE_HANDLES as cG, RIBBON_TAB_IDS as cH, RULER_FONT_SIZE as cI, RULER_THICKNESS as cJ, ReadingViewOverlayComponent as cK, RemoteSelectionOverlayComponent as cL, Rendering3DService as cM, RibbonAnimationGalleryComponent as cN, RibbonAnimationsSectionComponent as cO, RibbonArrangeSectionComponent as cP, RibbonColorPopoverComponent as cQ, RibbonComponent as cR, RibbonDesignSectionComponent as cS, RibbonDrawSectionComponent as cT, RibbonDrawingGroupComponent as cU, RibbonEditingSectionComponent as cV, RibbonFileSectionComponent as cW, RibbonFontControlsComponent as cX, RibbonHomeSectionComponent as cY, RibbonHyperlinkButtonComponent as cZ, RibbonInsertFieldsComponent as c_, OutlineViewOverlayComponent as ca, P14_TRANSITION_KEYFRAMES as cb, P14_TRANSITION_KEYFRAMES_2 as cc, P14_TRANSITION_KEYFRAMES_ALL as cd, POWER_POINT_VIEWER_PROVIDERS as ce, PPTX_OPEN_ACCEPT as cf, PRESENTATION_OPEN_EXTENSIONS as cg, PRESENTER_CHANNEL_NAME as ch, PRESENTER_MSG_ORIGIN as ci, PRESENTER_TIMER_SEGMENT_MS as cj, PX_PER_CM as ck, PX_PER_INCH as cl, PasswordProtectionDialogComponent as cm, PasswordStrengthMeterComponent as cn, PowerPointViewerComponent as co, PresentToolbarAutoHide as cp, PresentationAnnotationOverlayComponent as cq, PresentationAnnotationsService as cr, PresentationOverlayComponent as cs, PresentationPropertiesPanelComponent as ct, PresentationSettingsCardComponent as cu, PresentationSubtitleBarComponent as cv, PresentationToolbarComponent as cw, PresentationTransitionOverlayComponent as cx, PresenterViewComponent as cy, PresenterWindowService as cz, buildBarChart3DHoverTooltip as d, TableCellAdvancedFillComponent as d$, RibbonMotionPathGalleryComponent as d0, RibbonParagraphControlsComponent as d1, RibbonPrimaryRowComponent as d2, RibbonReviewSectionComponent as d3, RibbonShapeExtrasComponent as d4, RibbonSlideshowSectionComponent as d5, RibbonTransitionsSectionComponent as d6, RibbonViewSectionComponent as d7, RulerGuidesService as d8, SEQUENCE_OPTIONS as d9, SignaturesService as dA, SlideBackgroundCardComponent as dB, SlideCanvasComponent as dC, SlideDefaultInspectorComponent as dD, SlideDiffChangesComponent as dE, SlideDiffRowComponent as dF, SlideDiffThumbnailsComponent as dG, SlideSizeCardComponent as dH, SlideSorterOverlayComponent as dI, SlideThemeOverridePanelComponent as dJ, SlideTransitionCardComponent as dK, SlidesPanelComponent as dL, SmartArt3DRendererComponent as dM, SmartArtPreviewComponent as dN, SmartArtPropertiesComponent as dO, SmartArtRendererComponent as dP, StatusBarComponent as dQ, TABLE_STRUCTURE_TOGGLES as dR, TEXT_3D_BOTTOM_BEVEL_KEYS as dS, TEXT_3D_TOP_BEVEL_KEYS as dT, TEXT_DIRECTION_OPTIONS$1 as dU, THEME_CATALOG as dV, TIMING_CURVE_OPTIONS as dW, TOOLBAR_BUTTON_IDS as dX, TRANSITION_SPEED_DURATION_MS as dY, TRIGGER_OPTIONS as dZ, TYPE_LABELS as d_, SEVERITY_GROUPS as da, SEVERITY_LABELS as db, DEFAULT_TRANSITION_DURATION_MS$1 as dc, SHORTCUT_REFERENCE_ITEMS as dd, SLIDE_TRANSITION_KEYFRAMES as de, SLIDE_TRANSITION_KEYFRAMES_CSS as df, DEFAULT_PALETTE as dg, PALETTES as dh, SMART_ART_COLOR_SCHEMES as di, SMART_ART_STYLE_OPTIONS as dj, SUB_ITEM_LABEL as dk, SVG_WARP_PRESETS as dl, SWIPE_MAX_VERTICAL_PX as dm, SWIPE_THRESHOLD_PX as dn, SelectionPaneComponent as dp, SetUpSlideShowDialogComponent as dq, SettingsAppearanceTabComponent as dr, SettingsDialogComponent as ds, SettingsLanguageTabComponent as dt, ShareDialogComponent as du, ShortcutPanelComponent as dv, ShowOptionsFieldsetComponent as dw, ShowSlidesFieldsetComponent as dx, SignatureStrippedDialogComponent as dy, SignaturesPanelComponent as dz, roundDragValue as e, animationFor as e$, TableCellFormattingComponent as e0, TableDataEditorComponent as e1, TablePropertiesComponent as e2, TableRendererComponent as e3, TableResizeOverlayComponent as e4, TableSelectionService as e5, TagsCardComponent as e6, Text3DBevelSectionComponent as e7, Text3DPanelComponent as e8, TextAdvancedPanelComponent as e9, ViewerFileIOService as eA, ViewerFindReplaceService as eB, ViewerFormatPainterService as eC, ViewerInspectorPanelService as eD, ViewerKeyboardService as eE, ViewerMobileSheetService as eF, ViewerPresentationModeService as eG, ViewerThemeGalleryService as eH, ViewerTouchGesturesService as eI, ViewerZoomService as eJ, WEBM_MIME_CANDIDATES as eK, WHEEL_SPOKE_COUNTS as eL, WriteBackScheduler as eM, ZERO_LINE_COLOR as eN, ZoomNavigationService as eO, ZoomRendererComponent as eP, ZoomTargetService as eQ, addCategory as eR, addCommentToList as eS, addGradientStopPatch as eT, addItem as eU, addSeries as eV, addSubItem as eW, advanceStep as eX, affordanceElements as eY, aiToggleVisible as eZ, alignPatch as e_, ThemeEditorFieldsComponent as ea, ThemeGalleryComponent as eb, ThemeSelectorCardComponent as ec, TitleBarComponent as ed, TitleBarSearchComponent as ee, TransitionDirectionPickerComponent as ef, TransitionPreviewComponent as eg, VALIGN_OPTIONS as eh, VIEWER_DIALOG_IDS as ei, VIEWER_EXPORT_FORMAT_IDS as ej, VIEWER_FEATURE_IDS as ek, VIEWER_PANEL_IDS as el, VIEWER_THEME as em, VersionHistoryPanelComponent as en, ViewerCanvasEditingService as eo, ViewerCollabCursorService as ep, ViewerCollaborationSessionService as eq, ViewerCollaborationShellService as er, ViewerCompareService as es, ViewerCustomShowsService as et, ViewerCustomizationHandle as eu, ViewerCustomizationService as ev, ViewerDialogsService as ew, ViewerDocumentPropertiesService as ex, ViewerExportService as ey, ViewerExtraDialogsComponent as ez, buildPerspCamera as f, buildStockViewModel as f$, animationPresetLabelKey as f0, annotationMapToInkInserts as f1, applyAcceptedDiff as f2, applyAnimationPreset as f3, applyFindReplacements as f4, applyFormatToElement as f5, applyMove as f6, applyResize as f7, asMediaElement as f8, assignUserColor as f9, buildEmbeddedFontStyles as fA, buildEquationElement as fB, buildEquationSegment as fC, buildFallbackViewModel as fD, buildFontFaceRule as fE, buildGradientFillCss as fF, buildGridlinesAndLabels as fG, buildHyperlinkPatch as fH, buildInkContainerStyle as fI, buildInkStrokes as fJ, buildLegend as fK, buildLiveInkStrokeView as fL, buildMarkTooltip as fM, buildModel3DContainerStyle as fN, buildModel3DViewModel as fO, buildOleActionModel as fP, buildOleInfoRows as fQ, buildPatternFillCss as fR, buildPieViewModel as fS, buildPrintHtmlDocument as fT, buildPropertiesPatch as fU, buildRadarViewModel as fV, buildRegionMapViewModel as fW, buildSaveSlides as fX, buildShareUrl as fY, buildSmartArtInsertElement as fZ, buildSmartArtNodes as f_, attachShowVisibilityPause as fa, attachTouchGestures as fb, axisTickValues as fc, beginDrawingNodeEdit as fd, beginNodeEdit as fe, bevelSizePatch as ff, boolFromEvent as fg, bringForward as fh, bringToFront as fi, buildBarActions as fj, buildBroadcastConfig as fk, buildBroadcastViewerUrl as fl, buildCategoryLabels as fm, buildCellParagraphs as fn, buildChartViewModel as fo, buildChatLogExport as fp, buildChatLogMarkdown as fq, buildChromeStyle as fr, buildClearHyperlinkPatch as fs, buildClickGroups as ft, buildColStyles as fu, buildCollaborationConfig as fv, buildComboViewModel as fw, buildCssGradientFromShapeStyle as fx, buildDuotoneFilter as fy, buildDuotoneFilterId as fz, perspToScreen as g, computePlotLayout as g$, buildSurfaceViewModel as g0, buildTableViewModel as g1, buildTreemapViewModel as g2, buildTrimFragment as g3, buildWaterfallViewModel as g4, buildZeroLine as g5, buildZoomContainerStyle as g6, buildZoomViewModel as g7, bulletIndentPx as g8, canAddTopLevelNode as g9, collectAccessibilityIssues as gA, collectElementText as gB, collectSlideText as gC, collectStoredChats as gD, collectUsedFontFamilies as gE, columnWidthStyle as gF, commitNodeText as gG, computeAlign as gH, computeAxisTitlePrimitives as gI, computeBarRects as gJ, computeBubbleRadius as gK, computeCornerHandle as gL, computeDistribute as gM, computeDrawingViewBox as gN, computeErrorBarPrimitives as gO, computeFocusTargets as gP, computeGridSpacingPx as gQ, computeHandleBoxes as gR, computeHandoutLayout as gS, computeIsMobile as gT, computeIsTablet as gU, computeLinePoints as gV, computeLinearRegression as gW, computePageCount as gX, computePieLayout as gY, computePieSlicePath as gZ, computePieSlices as g_, canEditSmartArtNodes as ga, canGroupSelection as gb, canRemoveTopLevelNode as gc, canSetStrokeWidth as gd, canStartBroadcast as ge, canStartShare as gf, canUngroupSelection as gg, canUseClipboard as gh, captionDisplayText as gi, cellRunStyle as gj, cellStyleToStyleMap as gk, cellTdStyle as gl, changeCountLabel as gm, changeIcon as gn, characterSpacingPatch as go, chartPreserveAspectRatio as gp, checkFontAvailable as gq, clampCursorPosition as gr, clampGifDimensions as gs, clampIndex as gt, clampNotesFontSize as gu, clampScale as gv, clampStep as gw, clearAllLocalViewerData as gx, clearAudienceContent as gy, cn as gz, pieRimPoint as h, firstVisibleIndex as h$, computeRSquared as h0, computeRadarPoints as h1, computeResizeHandleBoxes as h2, computeRotateHandleBox as h3, computeScatterDots as h4, computeScatterXDomain as h5, computeSelectionBoxes as h6, computeSingleSelected as h7, computeSlideIndices as h8, computeSnap as h9, describeCollaborationShellState as hA, describeSmartArtBounds as hB, directionPickerState as hC, disableGlowPatch as hD, disableInnerShadowPatch as hE, disableOuterShadowPatch as hF, disableReflectionPatch as hG, disableSoftEdgePatch as hH, duplicateElementById as hI, durationOf as hJ, effectsStateOf as hK, enableGlowPatch as hL, enableInnerShadowPatch as hM, enableOuterShadowPatch as hN, enableReflectionPatch as hO, enableSoftEdgePatch as hP, encodeGif as hQ, endShowMediaCleanup as hR, estimatePageCount as hS, exitPresentationFullscreen as hT, exportAiChatLogs as hU, extractPathPoints as hV, eyedropperAvailable as hW, fillColorOf$1 as hX, findInSlides as hY, findOwningSlideIndex as hZ, findSlideIndexByElementId as h_, computeStackedBarRects as ha, computeStackedValueRange as hb, computeTrendlinePrimitives as hc, computeValueRange as hd, convertOmmlToMathMl as he, copyFormatFromElement as hf, countAccessibilityIssues as hg, countAnnotationStrokes as hh, createAngularAiBridge as hi, createCustomShow as hj, createImageElementFromFile as hk, createSwipeDismissDrag as hl, createWebrtcBundle as hm, createWebsocketBundle as hn, cssObjectToStyleMap as ho, currentColorScheme as hp, currentLayout as hq, currentStyle as hr, defaultCssVars as hs, defaultRadius as ht, defaultThemeColors as hu, deleteElementsByIds as hv, deleteVersion as hw, demoteNode as hx, deriveModel3DBlobUrl as hy, derivePresenceList as hz, isChartInteractionArmed as i, hasExitedFullscreen as i$, fitPolynomial as i0, fitZoom as i1, focusTargetChips as i2, fontMimeForFormat as i3, fontSizeOf as i4, forgetSessionDeck as i5, formatAxisValue as i6, formatBytes as i7, formatCursorLabel as i8, formatElapsed as i9, getP14TransitionAnimations as iA, getPasswordStrength as iB, getPatternSvg as iC, getPlaceholderStyle as iD, getVersions as iE, getResolvedShapeClipPath as iF, getResolvedShapeClipPathFor as iG, getSessionTabId as iH, getShapeFillStrokeStyle as iI, getSlideBackgroundStyle as iJ, getSlideTransitionAnimations as iK, getSmartArtNodeBounds as iL, getSpeechRecognitionCtor as iM, getTextBlockStyle as iN, getTextWarp as iO, getTouchDistance as iP, getWarpCategory as iQ, getWarpPath as iR, gradientStateFromStyle as iS, gradientStateOf as iT, gradientStatePatch as iU, gradientStopColorCommitPatch as iV, gridColumns as iW, groupIssuesBySeverity as iX, hasAnimation as iY, hasCopyableFormat as iZ, hasExistingLink as i_, formatFileSize as ia, formatInlineListStyle as ib, formatPropertyDate as ic, formatTime as id, fpsToFrameIntervalMs as ie, generateBroadcastRoomId as ig, generateCommentId as ih, generateCustomShowId as ii, generatePressureCircles as ij, generateTicks as ik, getCinematicTransitionAnimations as il, getClrChangeParams as im, getContainerStyle as io, getDuotoneFilterDef as ip, getEffectSoundAsset as iq, getEffectSoundState as ir, getImageSrc as is, getLocalStorageUsageSummary as it, getOleAriaLabel as iu, getOleBadgeLabel as iv, getOleDisplayName as iw, getOleDownloadFileName as ix, getOleTypeColor as iy, getOleTypeLabel as iz, computePieChartLayout as j, nodeEditBox as j$, hasGradientFill as j0, hasPressureVariation as j1, hasVisibleSlideAfter as j2, headerLabel as j3, imageDimensions as j4, injectResolvedCustomization as j5, inkViewBox as j6, insertTableElementColumn as j7, insertTableElementRow as j8, interpolateWidth as j9, linePointsToSvgString as jA, lineSpacingPatch as jB, loadAudienceContent as jC, loadSessionDeck as jD, mediaFallbackFor as jE, mediaSurfaceFor as jF, mergeCaptionResults as jG, mergeDown as jH, mergeRight as jI, mergeSelection as jJ, mergeTablesDirective as jK, moveElementBy as jL, moveNodeDown as jM, moveNodeUp as jN, msToFrameDelayCs as jO, narrowToCircle as jP, narrowToPolygon as jQ, narrowToRect as jR, newChartElement as jS, newEquationElement as jT, newPresetShapeElement as jU, newShapeElement as jV, newSmartArtElement as jW, newTableElement as jX, newTextElement as jY, nextVisibleIndex as jZ, nodeBold as j_, isAudienceTab as ja, isBold as jb, isBrowserOpenableMime as jc, isChildNode as jd, isElementInteractive as je, isInjectableUrl as jf, isItalic as jg, isLegacyBinaryPresentation as jh, isPpactionUrl as ji, isPresenterMessage as jj, isSigned as jk, isSupportedPresentationFile as jl, isTextElement as jm, isTwoTableFocus as jn, isUnderline as jo, isUrlSafe as jp, isValidRoomId as jq, isViewportBackgroundPressTarget as jr, isZoomActivationKey as js, issueTrackKey as jt, issueTypeLabel as ju, keyToLabel as jv, lastVisibleIndex as jw, latexToMathml as jx, layoutConnectorPaints as jy, layoutNodeLabels as jz, perspScreenToPlaneY as k, resizeElement as k$, nodeFillColor as k0, nodeFontColor as k1, nodeIdFromKey as k2, nodeItalic as k3, nodeStyle as k4, normalizeFontFormat as k5, normalizeSlidesPerPage as k6, normalizeValue as k7, numFromEvent as k8, oleActionsVisibleFor as k9, presetsForCategory as kA, pressuresToWidths as kB, prevVisibleIndex as kC, projectDrawingShapes as kD, promoteNode as kE, provideViewerTheme as kF, radarAngle as kG, radarRingPoints as kH, readAsDataUrl as kI, recordWebm as kJ, registerCrossSlideAudio as kK, rememberSessionDeck as kL, removeAnimation as kM, removeCategory as kN, removeTableElementColumn as kO, removeCommentFromList as kP, removeElementAnimation as kQ, removeGradientStopPatch as kR, removeNode as kS, removeTableElementRow as kT, removeSeries as kU, renderToCanvas as kV, reorderAnimationDown as kW, reorderAnimationUp as kX, replaceInSlides as kY, replaceMatch as kZ, requestPresentationFullscreen as k_, ommlToMathml as ka, ooxmlDashToCssBorderStyle as kb, openNativeEyeDropper as kc, overallStatus as kd, paletteColor as ke, parseAudienceNonce as kf, parseNodeTextarea as kg, partitionSlides as kh, patchChartData as ki, patchChartStyle as kj, patchTableData as kk, patchTextStyle as kl, patternPresetOptions as km, pendingElementStyles as kn, pickColorByClickFallback as ko, pickFile as kp, pickSupportedMimeType as kq, planGifFrames as kr, planVideoSegments as ks, pointFromPointerEvent as kt, pointsToSvgPathD as ku, presenceToCursors as kv, presentationBaseName as kw, presentationStageStyle as kx, presenterTimerProgress as ky, presetByLayout as kz, resolvePieSliceShareValue as l, setDataPointFill as l$, resolveCaptionTracks as l0, resolveChartKind as l1, resolveDirection as l2, resolveDirection8 as l3, resolveFontVariant as l4, resolveHyperlinkHref as l5, resolveInteractiveElementId as l6, resolveLegendAnchorPosition as l7, resolveMediaSrc as l8, resolveOleType as l9, scanAvailableFonts as lA, searchSlides as lB, seedBroadcastFields as lC, seedHyperlinkDraft as lD, seedPropertiesDraft as lE, seedShareFields as lF, segmentFrameCount as lG, selectValue$3 as lH, sendBackward as lI, sendToBack as lJ, sequentialColorScale as lK, serializeWriteBack as lL, seriesColor as lM, seriesXValues as lN, setAfterAnimation as lO, setAfterAnimationColor as lP, setAnimationEmphasis as lQ, setAnimationEntrance as lR, setAnimationExit as lS, setAxis as lT, setAxisLogScale as lU, setAxisTitleStyle as lV, setCategoryLabel as lW, setCellText as lX, setColorScheme as lY, setDataLabels as lZ, setDataPointExplosion as l_, resolveOrientation as la, resolveParagraphBullet as lb, resolvePresenterNotes as lc, resolveProfileInitial as ld, resolveRegionCode as le, resolveRibbonCanGroup as lf, resolveSlideAutoAdvanceMs as lg, resolveSlideTransition as lh, resolvePalette as li, resolveThemeCatalogEntry as lj, resolveTransitionDuration as lk, resolveTransitionDurationMs as ll, resolveWheelSpokeCount as lm, restoreSessionDeck as ln, revealedElementStyles as lo, routeOrthogonalConnector as lp, rowStyle as lq, rulerDragToGuidePosition as lr, rulerHighlight as ls, rulerStripTicks as lt, sampleColorFromSlide as lu, sanitizeColor as lv, sanitizeSlideIndex as lw, sanitizeUserName as lx, saveViewerProfile as ly, savedPresentationFileName as lz, createSurfaceChart3DScene as m, themeStyle as m$, setDataPointLabel as m0, setDataPointMarker as m1, setDelay as m2, setDirection as m3, setDuration as m4, setEffectSound as m5, setEffectStockSound as m6, setElementPosition as m7, setGridlineStyle as m8, setLayout as m9, signatureTimestamp as mA, signerName as mB, statusLabel as mC, slideNumberOf as mD, slidesWithReappliedLayout as mE, smartArtNodes as mF, paletteColour as mG, snapToGridStep as mH, splitCursorCell as mI, splitMergedCell as mJ, statusKind as mK, statusLabel$1 as mL, storeAudienceContent as mM, stringFromEvent$5 as mN, strokeColorOf as mO, strokeToInkElement as mP, strokeWidthOf as mQ, styleShadowFilter as mR, surfaceColor as mS, textAdvancedPatch as mT, textAdvancedStateFromStyle as mU, textAdvancedStateOf as mV, textColorOf as mW, textDirectionPatch as mX, textFontSizePatch as mY, textStyleOf as mZ, textStylePatch as m_, setLegend as ma, setNodeStyle as mb, setNodeText as mc, setRepeatCount as md, setRepeatMode as me, setSequence as mf, setSeriesChartType as mg, setSeriesColor as mh, setSeriesErrorBars as mi, setSeriesMarker as mj, setSeriesName as mk, setSeriesTrendline as ml, setSeriesValue as mm, setStyle as mn, setTimingCurve as mo, setTitle as mp, setTrigger as mq, setTriggerShapeId as mr, shapeStylePatch$1 as ms, sheetAfterNavigate as mt, shouldBlockClickAdvance as mu, shouldUseSvgWarp as mv, showDirectionPicker as mw, showsTemplateAffordance as mx, signatureCountLabel as my, signatureKey as mz, createPieChart3DScene as n, themeToCssVars as n0, thumbnailHeight as n1, thumbnailZoom as n2, toggleCommentResolvedInList as n3, toggleNodeBold as n4, toggleNodeItalic as n5, toggleSheet as n6, topLevelNodeCount as n7, transformSelectedTextCase as n8, translationsEn as n9, updateElementById as na, updateGlowPatch as nb, updateGradientStopPatch as nc, updateInnerShadowPatch as nd, updateOuterShadowPatch as ne, updateReflectionPatch as nf, vAlignPatch as ng, validatePassword as nh, validatePrintSettings as ni, validateRoomId as nj, valueToY as nk, vermilionDarkColors as nl, vermilionDarkTheme as nm, vermilionLightColors as nn, vermilionLightTheme as no, vermilionRadius as np, waypointsToPathD as nq, withManualLayouts as nr, worstStatus as ns, zoomTargetSlideIndex as nt, obliqueBarTaper as o, perspBoxMatrix as p, createAreaChart3DScene as q, renderChartLineSvg as r, shade as s, toChatSummary as t, createLineChart3DScene as u, createBarChart3DScene as v, widenPieViewModel as w, hexToRgb as x, smartArt3DGradientColor as y, shadeSmartArt3DVertices as z };
214969
- //# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-DvjSjGWC.mjs.map
211976
+ export { AiProposalCardComponent as $, smartArt3DEyeInDiagram as A, resolveSmartArt3DLightModel as B, AFTER_ANIMATION_VALUES as C, DEFAULT_PALETTE$1 as D, ALIGN_OPTIONS as E, ANIMATION_PRESET_CATEGORIES as F, AUDIENCE_HASH as G, AUDIENCE_NONCE_KEY as H, AVATAR_COLOR_SWATCHES as I, AXIS_LABEL_COLOR as J, AccessibilityPanelComponent as K, AccessibilityService as L, AccessibilityTextPanelComponent as M, NO_THREE_VIEW_OVERFLOW as N, AccountPageComponent as O, ActionSettingsPanelComponent as P, AdvancedChartEditorComponent as Q, AiChangeOverlayComponent as R, AiChatPanelComponent as S, AiChatService as T, AiComposerComponent as U, AiFocusBarComponent as V, AiFocusHighlightOverlayComponent as W, AiHistoryMenuComponent as X, AiHistoryService as Y, AiMessageListComponent as Z, AiPanelStore as _, appendChartLegendSvg as a, ELEMENT_CONTEXT_MENU_COMMAND_IDS as a$, AiSettingsSectionComponent as a0, AiToolCallCardComponent as a1, AnimationAuthorPanelComponent as a2, AnimationPanelComponent as a3, AnimationPlaybackService as a4, AutosaveRecoveryDialogComponent as a5, AutosaveService as a6, BACKSTAGE_CARD_IDS as a7, BACKSTAGE_PAGE_IDS as a8, BroadcastDialogComponent as a9, CommentsService as aA, ComparePanelComponent as aB, ConnectorRendererComponent as aC, ConnectorTextOverlayComponent as aD, CustomShowsComponent as aE, DEFAULT_BOUNDS as aF, DEFAULT_BROADCAST_SERVER_URL as aG, DEFAULT_CANVAS_HEIGHT as aH, DEFAULT_CANVAS_WIDTH as aI, DEFAULT_COLOR_SCHEME as aJ, DEFAULT_FILL_COLOR$1 as aK, DEFAULT_LAYOUT as aL, DEFAULT_MORPH_DURATION_MS as aM, DEFAULT_PATTERN_FILL_PRESET as aN, DEFAULT_PRINT_SETTINGS as aO, DEFAULT_SLIDE_BACKGROUND as aP, DEFAULT_STROKE_COLOR as aQ, DEFAULT_STYLE as aR, DEFAULT_TABLE_ROW_HEIGHT as aS, DEFAULT_TEXT_COLOR$2 as aT, DEFAULT_VIEWER_PROFILE as aU, DIRECTIONAL_PRESETS as aV, DIRECTION_OPTIONS as aW, DocumentPropertiesCardComponent as aX, EASE as aY, EDITOR_SHORTCUT_ACTION_IDS as aZ, EFFECT_SOUND_CATALOGUE as a_, CANVAS_CONTEXT_MENU_COMMAND_IDS as aa, CHART_EDITOR_STYLES as ab, CINEMATIC_TRANSITION_KEYFRAMES as ac, CURSOR_PALETTE as ad, CanvasFitService as ae, ChartAxisOptionsComponent as af, ChartAxisStyleOptionsComponent as ag, ChartComboTypeOptionsComponent as ah, ChartDataEditorComponent as ai, ChartDataLabelOptionsComponent as aj, ChartDatapointMarkerOptionsComponent as ak, ChartDatapointOptionsComponent as al, ChartDisplayOptionsComponent as am, ChartElementViewComponent as an, ChartErrorBarOptionsComponent as ao, ChartMarkerOptionsComponent as ap, ChartPartSelectionService as aq, ChartPrimitivesComponent as ar, ChartRendererComponent as as, ChartTrendlineOptionsComponent as at, ChartTypeSelectorComponent as au, CollaborationCursorsComponent as av, CollaborationService as aw, ColorChangedImageComponent as ax, CommentMarkersOverlayComponent as ay, CommentsPanelComponent as az, renderChartTextSvg as b, NotesToolbarComponent as b$, EMBEDDED_FONTS_STYLE_ID as b0, EMPHASIS_PRESETS as b1, ENTRANCE_PRESETS as b2, TEMPLATES as b3, EXIT_PRESETS as b4, EditorContextMenuComponent as b5, EditorHistory as b6, EditorStateService as b7, EditorToolbarComponent as b8, EffectsPanelComponent as b9, InsertSmartArtDialogComponent as bA, InspectorPaneHeaderComponent as bB, InspectorPanelComponent as bC, IsMobileService as bD, KeepAnnotationsDialogComponent as bE, LOCALE_CATALOG as bF, LONG_PRESS_DURATION_MS as bG, LONG_PRESS_MOVE_TOLERANCE_PX as bH, LoadContentService as bI, LocalPresencePublisher as bJ, MAX_ZOOM_SCALE as bK, MIN_ZOOM_SCALE as bL, MOTION_PATH_COLUMNS as bM, MediaPreviewComponent as bN, MediaPropertiesPanelComponent as bO, MediaRendererComponent as bP, MediaTrimTimelineComponent as bQ, MobileBottomBarComponent as bR, MobileMenuSheetComponent as bS, MobilePresenterViewComponent as bT, MobileSheetComponent as bU, MobileSlidesSheetComponent as bV, MobileToolbarComponent as bW, ModalDialogComponent as bX, Model3DRendererComponent as bY, NotesHandoutCardComponent as bZ, NotesPanelComponent as b_, ElementRendererComponent as ba, EmbeddedFontsService as bb, EncryptedFileDialogComponent as bc, EquationEditorDialogComponent as bd, EquationRendererComponent as be, EquationTemplateGalleryComponent as bf, ExportProgressModalComponent as bg, ExportService as bh, FieldContextService as bi, FindBarComponent as bj, FindReplaceBarComponent as bk, FollowModeBarComponent as bl, FontEmbeddingListComponent as bm, FontEmbeddingPanelComponent as bn, GALLERY_THEME_PRESETS as bo, GOOGLE_WEBFONTS_LINK_ID as bp, GRIDLINE_COLOR$1 as bq, GoogleWebfontsService as br, GradientPickerComponent as bs, HANDOUT_OPTIONS as bt, HeaderFooterDialogComponent as bu, HyperlinkDialogComponent as bv, INSTANT as bw, ImagePropertiesPanelComponent as bx, InkDrawingService as by, InkRendererComponent as bz, createSvgEl as c, RibbonSlideshowSectionComponent as c$, OPTIONS_PAGE_IDS as c0, OPTIONS_SECTION_IDS as c1, OPTIONS_SETTING_IDS as c2, OleRendererComponent as c3, OutlineViewOverlayComponent as c4, P14_TRANSITION_KEYFRAMES as c5, P14_TRANSITION_KEYFRAMES_2 as c6, P14_TRANSITION_KEYFRAMES_ALL as c7, POWER_POINT_VIEWER_PROVIDERS as c8, PPTX_OPEN_ACCEPT as c9, RESIZE_HANDLES as cA, RIBBON_TAB_IDS as cB, RULER_FONT_SIZE as cC, RULER_THICKNESS as cD, ReadingViewOverlayComponent as cE, RemoteSelectionOverlayComponent as cF, Rendering3DService as cG, RibbonAnimationGalleryComponent as cH, RibbonAnimationsSectionComponent as cI, RibbonArrangeSectionComponent as cJ, RibbonColorPopoverComponent as cK, RibbonComponent as cL, RibbonDesignSectionComponent as cM, RibbonDrawSectionComponent as cN, RibbonDrawingGroupComponent as cO, RibbonEditingSectionComponent as cP, RibbonFileSectionComponent as cQ, RibbonFontControlsComponent as cR, RibbonHomeSectionComponent as cS, RibbonHyperlinkButtonComponent as cT, RibbonInsertFieldsComponent as cU, RibbonInsertSectionComponent as cV, RibbonMotionPathGalleryComponent as cW, RibbonParagraphControlsComponent as cX, RibbonPrimaryRowComponent as cY, RibbonReviewSectionComponent as cZ, RibbonShapeExtrasComponent as c_, PRESENTATION_OPEN_EXTENSIONS as ca, PRESENTER_CHANNEL_NAME as cb, PRESENTER_MSG_ORIGIN as cc, PRESENTER_TIMER_SEGMENT_MS as cd, PX_PER_CM as ce, PX_PER_INCH as cf, PasswordProtectionDialogComponent as cg, PasswordStrengthMeterComponent as ch, PowerPointViewerComponent as ci, PresentToolbarAutoHide as cj, PresentationAnnotationOverlayComponent as ck, PresentationAnnotationsService as cl, PresentationOverlayComponent as cm, PresentationPropertiesPanelComponent as cn, PresentationSettingsCardComponent as co, PresentationSubtitleBarComponent as cp, PresentationToolbarComponent as cq, PresentationTransitionOverlayComponent as cr, PresenterViewComponent as cs, PresenterWindowService as ct, PrintDialogComponent as cu, PrintService as cv, PrintSettingsPanelComponent as cw, PropertiesDialogComponent as cx, RANDOM_ELIGIBLE_TYPES as cy, REPEAT_MODE_OPTIONS as cz, buildBarChart3DHoverTooltip as d, TableSelectionService as d$, RibbonTransitionsSectionComponent as d0, RibbonViewSectionComponent as d1, RulerGuidesService as d2, SEQUENCE_OPTIONS as d3, SEVERITY_GROUPS as d4, SEVERITY_LABELS as d5, DEFAULT_TRANSITION_DURATION_MS$1 as d6, SHORTCUT_REFERENCE_ITEMS as d7, SLIDE_TRANSITION_KEYFRAMES as d8, SLIDE_TRANSITION_KEYFRAMES_CSS as d9, SlideDiffThumbnailsComponent as dA, SlideSizeCardComponent as dB, SlideSorterOverlayComponent as dC, SlideThemeOverridePanelComponent as dD, SlideTransitionCardComponent as dE, SlidesPanelComponent as dF, SmartArt3DRendererComponent as dG, SmartArtPreviewComponent as dH, SmartArtPropertiesComponent as dI, SmartArtRendererComponent as dJ, StatusBarComponent as dK, TABLE_STRUCTURE_TOGGLES as dL, TEXT_3D_BOTTOM_BEVEL_KEYS as dM, TEXT_3D_TOP_BEVEL_KEYS as dN, TEXT_DIRECTION_OPTIONS$1 as dO, THEME_CATALOG as dP, TIMING_CURVE_OPTIONS as dQ, TOOLBAR_BUTTON_IDS as dR, TRANSITION_SPEED_DURATION_MS as dS, TRIGGER_OPTIONS as dT, TYPE_LABELS as dU, TableCellAdvancedFillComponent as dV, TableCellFormattingComponent as dW, TableDataEditorComponent as dX, TablePropertiesComponent as dY, TableRendererComponent as dZ, TableResizeOverlayComponent as d_, DEFAULT_PALETTE as da, PALETTES as db, SMART_ART_COLOR_SCHEMES as dc, SMART_ART_STYLE_OPTIONS as dd, SUB_ITEM_LABEL as de, SVG_WARP_PRESETS as df, SWIPE_MAX_VERTICAL_PX as dg, SWIPE_THRESHOLD_PX as dh, SelectionPaneComponent as di, SetUpSlideShowDialogComponent as dj, SettingsAppearanceTabComponent as dk, SettingsDialogComponent as dl, SettingsLanguageTabComponent as dm, ShareDialogComponent as dn, ShortcutPanelComponent as dp, ShowOptionsFieldsetComponent as dq, ShowSlidesFieldsetComponent as dr, SignatureStrippedDialogComponent as ds, SignaturesPanelComponent as dt, SignaturesService as du, SlideBackgroundCardComponent as dv, SlideCanvasComponent as dw, SlideDefaultInspectorComponent as dx, SlideDiffChangesComponent as dy, SlideDiffRowComponent as dz, roundDragValue as e, applyFormatToElement as e$, TagsCardComponent as e0, Text3DBevelSectionComponent as e1, Text3DPanelComponent as e2, TextAdvancedPanelComponent as e3, ThemeEditorFieldsComponent as e4, ThemeGalleryComponent as e5, ThemeSelectorCardComponent as e6, TitleBarComponent as e7, TitleBarSearchComponent as e8, TransitionDirectionPickerComponent as e9, ViewerPresentationModeService as eA, ViewerThemeGalleryService as eB, ViewerTouchGesturesService as eC, ViewerZoomService as eD, WEBM_MIME_CANDIDATES as eE, WHEEL_SPOKE_COUNTS as eF, WriteBackScheduler as eG, ZERO_LINE_COLOR as eH, ZoomNavigationService as eI, ZoomRendererComponent as eJ, ZoomTargetService as eK, addCategory as eL, addCommentToList as eM, addGradientStopPatch as eN, addItem as eO, addSeries as eP, addSubItem as eQ, advanceStep as eR, affordanceElements as eS, aiToggleVisible as eT, alignPatch as eU, animationFor as eV, animationPresetLabelKey as eW, annotationMapToInkInserts as eX, applyAcceptedDiff as eY, applyAnimationPreset as eZ, applyFindReplacements as e_, TransitionPreviewComponent as ea, VALIGN_OPTIONS as eb, VIEWER_DIALOG_IDS as ec, VIEWER_EXPORT_FORMAT_IDS as ed, VIEWER_FEATURE_IDS as ee, VIEWER_PANEL_IDS as ef, VIEWER_THEME as eg, VersionHistoryPanelComponent as eh, ViewerCanvasEditingService as ei, ViewerCollabCursorService as ej, ViewerCollaborationSessionService as ek, ViewerCollaborationShellService as el, ViewerCompareService as em, ViewerCustomShowsService as en, ViewerCustomizationHandle as eo, ViewerCustomizationService as ep, ViewerDialogsService as eq, ViewerDocumentPropertiesService as er, ViewerExportService as es, ViewerExtraDialogsComponent as et, ViewerFileIOService as eu, ViewerFindReplaceService as ev, ViewerFormatPainterService as ew, ViewerInspectorPanelService as ex, ViewerKeyboardService as ey, ViewerMobileSheetService as ez, buildPerspCamera as f, buildZeroLine as f$, applyMove as f0, applyResize as f1, asMediaElement as f2, assignUserColor as f3, attachShowVisibilityPause as f4, attachTouchGestures as f5, axisTickValues as f6, beginDrawingNodeEdit as f7, beginNodeEdit as f8, bevelSizePatch as f9, buildGridlinesAndLabels as fA, buildHyperlinkPatch as fB, buildInkContainerStyle as fC, buildInkStrokes as fD, buildLegend as fE, buildLiveInkStrokeView as fF, buildMarkTooltip as fG, buildModel3DContainerStyle as fH, buildModel3DViewModel as fI, buildOleActionModel as fJ, buildOleInfoRows as fK, buildPatternFillCss as fL, buildPieViewModel as fM, buildPrintHtmlDocument as fN, buildPropertiesPatch as fO, buildRadarViewModel as fP, buildRegionMapViewModel as fQ, buildSaveSlides as fR, buildShareUrl as fS, buildSmartArtInsertElement as fT, buildSmartArtNodes as fU, buildStockViewModel as fV, buildSurfaceViewModel as fW, buildTableViewModel as fX, buildTreemapViewModel as fY, buildTrimFragment as fZ, buildWaterfallViewModel as f_, boolFromEvent as fa, bringForward as fb, bringToFront as fc, buildBarActions as fd, buildBroadcastConfig as fe, buildBroadcastViewerUrl as ff, buildCategoryLabels as fg, buildCellParagraphs as fh, buildChartViewModel as fi, buildChatLogExport as fj, buildChatLogMarkdown as fk, buildChromeStyle as fl, buildClearHyperlinkPatch as fm, buildClickGroups as fn, buildColStyles as fo, buildCollaborationConfig as fp, buildComboViewModel as fq, buildCssGradientFromShapeStyle as fr, buildDuotoneFilter as fs, buildDuotoneFilterId as ft, buildEmbeddedFontStyles as fu, buildEquationElement as fv, buildEquationSegment as fw, buildFallbackViewModel as fx, buildFontFaceRule as fy, buildGradientFillCss as fz, perspToScreen as g, computeScatterXDomain as g$, buildZoomContainerStyle as g0, buildZoomViewModel as g1, bulletIndentPx as g2, canAddTopLevelNode as g3, canEditSmartArtNodes as g4, canGroupSelection as g5, canRemoveTopLevelNode as g6, canSetStrokeWidth as g7, canStartBroadcast as g8, canStartShare as g9, commitNodeText as gA, computeAlign as gB, computeAxisTitlePrimitives as gC, computeBarRects as gD, computeBubbleRadius as gE, computeCornerHandle as gF, computeDistribute as gG, computeDrawingViewBox as gH, computeErrorBarPrimitives as gI, computeFocusTargets as gJ, computeGridSpacingPx as gK, computeHandleBoxes as gL, computeHandoutLayout as gM, computeIsMobile as gN, computeIsTablet as gO, computeLinePoints as gP, computeLinearRegression as gQ, computePageCount as gR, computePieLayout as gS, computePieSlicePath as gT, computePieSlices as gU, computePlotLayout as gV, computeRSquared as gW, computeRadarPoints as gX, computeResizeHandleBoxes as gY, computeRotateHandleBox as gZ, computeScatterDots as g_, canUngroupSelection as ga, canUseClipboard as gb, captionDisplayText as gc, cellRunStyle as gd, cellStyleToStyleMap as ge, cellTdStyle as gf, changeCountLabel as gg, changeIcon as gh, characterSpacingPatch as gi, chartPreserveAspectRatio as gj, checkFontAvailable as gk, clampCursorPosition as gl, clampGifDimensions as gm, clampIndex as gn, clampNotesFontSize as go, clampScale as gp, clampStep as gq, clearAllLocalViewerData as gr, clearAudienceContent as gs, cn as gt, collectAccessibilityIssues as gu, collectElementText as gv, collectSlideText as gw, collectStoredChats as gx, collectUsedFontFamilies as gy, columnWidthStyle as gz, pieRimPoint as h, forgetSessionDeck as h$, computeSelectionBoxes as h0, computeSingleSelected as h1, computeSlideIndices as h2, computeSnap as h3, computeStackedBarRects as h4, computeStackedValueRange as h5, computeTrendlinePrimitives as h6, computeValueRange as h7, convertOmmlToMathMl as h8, copyFormatFromElement as h9, disableReflectionPatch as hA, disableSoftEdgePatch as hB, duplicateElementById as hC, durationOf as hD, effectsStateOf as hE, enableGlowPatch as hF, enableInnerShadowPatch as hG, enableOuterShadowPatch as hH, enableReflectionPatch as hI, enableSoftEdgePatch as hJ, encodeGif as hK, endShowMediaCleanup as hL, estimatePageCount as hM, exitPresentationFullscreen as hN, exportAiChatLogs as hO, extractPathPoints as hP, eyedropperAvailable as hQ, fillColorOf$1 as hR, findInSlides as hS, findOwningSlideIndex as hT, findSlideIndexByElementId as hU, firstVisibleIndex as hV, fitPolynomial as hW, fitZoom as hX, focusTargetChips as hY, fontMimeForFormat as hZ, fontSizeOf as h_, countAccessibilityIssues as ha, countAnnotationStrokes as hb, createAngularAiBridge as hc, createCustomShow as hd, createImageElementFromFile as he, createSwipeDismissDrag as hf, createWebrtcBundle as hg, createWebsocketBundle as hh, cssObjectToStyleMap as hi, currentColorScheme as hj, currentLayout as hk, currentStyle as hl, defaultCssVars as hm, defaultRadius as hn, defaultThemeColors as ho, deleteElementsByIds as hp, deleteVersion as hq, demoteNode as hr, deriveModel3DBlobUrl as hs, derivePresenceList as ht, describeCollaborationShellState as hu, describeSmartArtBounds as hv, directionPickerState as hw, disableGlowPatch as hx, disableInnerShadowPatch as hy, disableOuterShadowPatch as hz, isChartInteractionArmed as i, injectResolvedCustomization as i$, formatAxisValue as i0, formatBytes as i1, formatCursorLabel as i2, formatElapsed as i3, formatFileSize as i4, formatInlineListStyle as i5, formatPropertyDate as i6, formatTime as i7, fpsToFrameIntervalMs as i8, generateBroadcastRoomId as i9, getResolvedShapeClipPathFor as iA, getSessionTabId as iB, getShapeFillStrokeStyle as iC, getSlideBackgroundStyle as iD, getSlideTransitionAnimations as iE, getSmartArtNodeBounds as iF, getSpeechRecognitionCtor as iG, getTextBlockStyle as iH, getTextWarp as iI, getTouchDistance as iJ, getWarpCategory as iK, getWarpPath as iL, gradientStateFromStyle as iM, gradientStateOf as iN, gradientStatePatch as iO, gradientStopColorCommitPatch as iP, gridColumns as iQ, groupIssuesBySeverity as iR, hasAnimation as iS, hasCopyableFormat as iT, hasExistingLink as iU, hasExitedFullscreen as iV, hasGradientFill as iW, hasPressureVariation as iX, hasVisibleSlideAfter as iY, headerLabel as iZ, imageDimensions as i_, generateCommentId as ia, generateCustomShowId as ib, generatePressureCircles as ic, generateTicks as id, getCinematicTransitionAnimations as ie, getClrChangeParams as ig, getContainerStyle as ih, getDuotoneFilterDef as ii, getEffectSoundAsset as ij, getEffectSoundState as ik, getImageSrc as il, getLocalStorageUsageSummary as im, getOleAriaLabel as io, getOleBadgeLabel as ip, getOleDisplayName as iq, getOleDownloadFileName as ir, getOleTypeColor as is, getOleTypeLabel as it, getP14TransitionAnimations as iu, getPasswordStrength as iv, getPatternSvg as iw, getPlaceholderStyle as ix, getVersions as iy, getResolvedShapeClipPath as iz, computePieChartLayout as j, normalizeFontFormat as j$, inkViewBox as j0, insertTableElementColumn as j1, insertTableElementRow as j2, interpolateWidth as j3, isAudienceTab as j4, isBold as j5, isBrowserOpenableMime as j6, isChildNode as j7, isElementInteractive as j8, isInjectableUrl as j9, mergeCaptionResults as jA, mergeDown as jB, mergeRight as jC, mergeSelection as jD, mergeTablesDirective as jE, moveElementBy as jF, moveNodeDown as jG, moveNodeUp as jH, msToFrameDelayCs as jI, narrowToCircle as jJ, narrowToPolygon as jK, narrowToRect as jL, newChartElement as jM, newEquationElement as jN, newPresetShapeElement as jO, newShapeElement as jP, newSmartArtElement as jQ, newTableElement as jR, newTextElement as jS, nextVisibleIndex as jT, nodeBold as jU, nodeEditBox as jV, nodeFillColor as jW, nodeFontColor as jX, nodeIdFromKey as jY, nodeItalic as jZ, nodeStyle as j_, isItalic as ja, isLegacyBinaryPresentation as jb, isPpactionUrl as jc, isPresenterMessage as jd, isSigned as je, isSupportedPresentationFile as jf, isTextElement as jg, isTwoTableFocus as jh, isUnderline as ji, isUrlSafe as jj, isValidRoomId as jk, isViewportBackgroundPressTarget as jl, isZoomActivationKey as jm, issueTrackKey as jn, issueTypeLabel as jo, keyToLabel as jp, lastVisibleIndex as jq, latexToMathml as jr, layoutConnectorPaints as js, layoutNodeLabels as jt, linePointsToSvgString as ju, lineSpacingPatch as jv, loadAudienceContent as jw, loadSessionDeck as jx, mediaFallbackFor as jy, mediaSurfaceFor as jz, perspScreenToPlaneY as k, resolveHyperlinkHref as k$, normalizeSlidesPerPage as k0, normalizeValue as k1, numFromEvent as k2, oleActionsVisibleFor as k3, ommlToMathml as k4, ooxmlDashToCssBorderStyle as k5, openNativeEyeDropper as k6, overallStatus as k7, paletteColor as k8, parseAudienceNonce as k9, radarAngle as kA, radarRingPoints as kB, readAsDataUrl as kC, recordWebm as kD, registerCrossSlideAudio as kE, rememberSessionDeck as kF, removeAnimation as kG, removeCategory as kH, removeTableElementColumn as kI, removeCommentFromList as kJ, removeElementAnimation as kK, removeGradientStopPatch as kL, removeNode as kM, removeTableElementRow as kN, removeSeries as kO, renderToCanvas as kP, reorderAnimationDown as kQ, reorderAnimationUp as kR, replaceInSlides as kS, replaceMatch as kT, requestPresentationFullscreen as kU, resizeElement as kV, resolveCaptionTracks as kW, resolveChartKind as kX, resolveDirection as kY, resolveDirection8 as kZ, resolveFontVariant as k_, parseNodeTextarea as ka, partitionSlides as kb, patchChartData as kc, patchChartStyle as kd, patchTableData as ke, patchTextStyle as kf, patternPresetOptions as kg, pendingElementStyles as kh, pickColorByClickFallback as ki, pickFile as kj, pickSupportedMimeType as kk, planGifFrames as kl, planVideoSegments as km, pointFromPointerEvent as kn, pointsToSvgPathD as ko, presenceToCursors as kp, presentationBaseName as kq, presentationStageStyle as kr, presenterTimerProgress as ks, presetByLayout as kt, presetsForCategory as ku, pressuresToWidths as kv, prevVisibleIndex as kw, projectDrawingShapes as kx, promoteNode as ky, provideViewerTheme as kz, resolvePieSliceShareValue as l, setEffectSound as l$, resolveInteractiveElementId as l0, resolveLegendAnchorPosition as l1, resolveMediaSrc as l2, resolveOleType as l3, resolveOrientation as l4, resolveParagraphBullet as l5, resolvePresenterNotes as l6, resolveProfileInitial as l7, resolveRegionCode as l8, resolveRibbonCanGroup as l9, segmentFrameCount as lA, selectValue$3 as lB, sendBackward as lC, sendToBack as lD, sequentialColorScale as lE, serializeWriteBack as lF, seriesColor as lG, seriesXValues as lH, setAfterAnimation as lI, setAfterAnimationColor as lJ, setAnimationEmphasis as lK, setAnimationEntrance as lL, setAnimationExit as lM, setAxis as lN, setAxisLogScale as lO, setAxisTitleStyle as lP, setCategoryLabel as lQ, setCellText as lR, setColorScheme as lS, setDataLabels as lT, setDataPointExplosion as lU, setDataPointFill as lV, setDataPointLabel as lW, setDataPointMarker as lX, setDelay as lY, setDirection as lZ, setDuration as l_, resolveSlideAutoAdvanceMs as la, resolveSlideTransition as lb, resolvePalette as lc, resolveThemeCatalogEntry as ld, resolveTransitionDuration as le, resolveTransitionDurationMs as lf, resolveWheelSpokeCount as lg, restoreSessionDeck as lh, revealedElementStyles as li, routeOrthogonalConnector as lj, rowStyle as lk, rulerDragToGuidePosition as ll, rulerHighlight as lm, rulerStripTicks as ln, sampleColorFromSlide as lo, sanitizeColor as lp, sanitizeSlideIndex as lq, sanitizeUserName as lr, saveViewerProfile as ls, savedPresentationFileName as lt, scanAvailableFonts as lu, searchSlides as lv, seedBroadcastFields as lw, seedHyperlinkDraft as lx, seedPropertiesDraft as ly, seedShareFields as lz, hexToRgb as m, toggleNodeItalic as m$, setEffectStockSound as m0, setElementPosition as m1, setGridlineStyle as m2, setLayout as m3, setLegend as m4, setNodeStyle as m5, setNodeText as m6, setRepeatCount as m7, setRepeatMode as m8, setSequence as m9, paletteColour as mA, snapToGridStep as mB, splitCursorCell as mC, splitMergedCell as mD, statusKind as mE, statusLabel$1 as mF, storeAudienceContent as mG, stringFromEvent$5 as mH, strokeColorOf as mI, strokeToInkElement as mJ, strokeWidthOf as mK, styleShadowFilter as mL, surfaceColor as mM, textAdvancedPatch as mN, textAdvancedStateFromStyle as mO, textAdvancedStateOf as mP, textColorOf as mQ, textDirectionPatch as mR, textFontSizePatch as mS, textStyleOf as mT, textStylePatch as mU, themeStyle as mV, themeToCssVars as mW, thumbnailHeight as mX, thumbnailZoom as mY, toggleCommentResolvedInList as mZ, toggleNodeBold as m_, setSeriesChartType as ma, setSeriesColor as mb, setSeriesErrorBars as mc, setSeriesMarker as md, setSeriesName as me, setSeriesTrendline as mf, setSeriesValue as mg, setStyle as mh, setTimingCurve as mi, setTitle as mj, setTrigger as mk, setTriggerShapeId as ml, shapeStylePatch$1 as mm, sheetAfterNavigate as mn, shouldBlockClickAdvance as mo, shouldUseSvgWarp as mp, showDirectionPicker as mq, showsTemplateAffordance as mr, signatureCountLabel as ms, signatureKey as mt, signatureTimestamp as mu, signerName as mv, statusLabel as mw, slideNumberOf as mx, slidesWithReappliedLayout as my, smartArtNodes as mz, smartArt3DGradientColor as n, toggleSheet as n0, topLevelNodeCount as n1, transformSelectedTextCase as n2, translationsEn as n3, updateElementById as n4, updateGlowPatch as n5, updateGradientStopPatch as n6, updateInnerShadowPatch as n7, updateOuterShadowPatch as n8, updateReflectionPatch as n9, vAlignPatch as na, validatePassword as nb, validatePrintSettings as nc, validateRoomId as nd, valueToY as ne, vermilionDarkColors as nf, vermilionDarkTheme as ng, vermilionLightColors as nh, vermilionLightTheme as ni, vermilionRadius as nj, waypointsToPathD as nk, withManualLayouts as nl, worstStatus as nm, zoomTargetSlideIndex as nn, obliqueBarTaper as o, perspBoxMatrix as p, shadeSmartArt3DVertices as q, renderChartLineSvg as r, shade as s, toChatSummary as t, smartArt3DGradientCoordinate as u, threeViewOverflowFromNdc as v, widenPieViewModel as w, hasThreeViewOverflow as x, overflowViewOffset as y, smartArt3DCameraMatrix as z };
211977
+ //# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-DMrqbfPM.mjs.map