pptx-angular-viewer 4.10.1 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/fesm2022/{pptx-angular-viewer-chart-3d-view-scene-CreE1jB0.mjs → pptx-angular-viewer-chart-3d-view-scene-Cfk7dMVK.mjs} +111 -24
- package/fesm2022/pptx-angular-viewer-chart-3d-view-scene-Cfk7dMVK.mjs.map +1 -0
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-D8QI7rBh.mjs → pptx-angular-viewer-chat-history-idb-BnV3ANp_.mjs} +2 -2
- package/fesm2022/{pptx-angular-viewer-chat-history-idb-D8QI7rBh.mjs.map → pptx-angular-viewer-chat-history-idb-BnV3ANp_.mjs.map} +1 -1
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-DvjSjGWC.mjs → pptx-angular-viewer-pptx-angular-viewer-Dy62s3Dt.mjs} +70 -3117
- package/fesm2022/{pptx-angular-viewer-pptx-angular-viewer-DvjSjGWC.mjs.map → pptx-angular-viewer-pptx-angular-viewer-Dy62s3Dt.mjs.map} +1 -1
- package/fesm2022/{pptx-angular-viewer-view-scene-DADbVF7v.mjs → pptx-angular-viewer-view-scene-CNlGt3WC.mjs} +2 -2
- package/fesm2022/{pptx-angular-viewer-view-scene-DADbVF7v.mjs.map → pptx-angular-viewer-view-scene-CNlGt3WC.mjs.map} +1 -1
- package/fesm2022/pptx-angular-viewer.mjs +1 -1
- package/package.json +2 -2
- package/types/pptx-angular-viewer.d.ts +18 -502
- package/types/pptx-angular-viewer.d.ts.map +1 -1
- package/fesm2022/pptx-angular-viewer-chart-3d-view-scene-CreE1jB0.mjs.map +0 -1
|
@@ -387,13 +387,25 @@ function renderSelectMenu(menu, choices, value) {
|
|
|
387
387
|
});
|
|
388
388
|
menu.replaceChildren(...menuItems);
|
|
389
389
|
}
|
|
390
|
-
/**
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
797
|
-
: this.choices
|
|
798
|
-
|
|
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.
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
7310
|
+
const normalizedRadius = clamp$7(radius, 0, 100) / 100;
|
|
7301
7311
|
switch (effectName) {
|
|
7302
7312
|
case 'artisticFilmGrain':
|
|
7303
7313
|
case 'filmGrain': {
|
|
@@ -19801,7 +19811,7 @@ function buildAreas(chartData, catCount, layout, range, sourceIndices, xPosition
|
|
|
19801
19811
|
return { primitives: [...primitives, ...labelBoxes], dataLabels };
|
|
19802
19812
|
}
|
|
19803
19813
|
|
|
19804
|
-
function clamp$
|
|
19814
|
+
function clamp$6(value, min, max) {
|
|
19805
19815
|
return Math.min(Math.max(value, min), max);
|
|
19806
19816
|
}
|
|
19807
19817
|
/** Resolve where the horizontal category/date axis crosses its linked value axis. */
|
|
@@ -19814,7 +19824,7 @@ function horizontalAxisY(valueAxis, range, layout, fallback) {
|
|
|
19814
19824
|
}
|
|
19815
19825
|
const crossingValue = valueAxis?.crossesAt ?? 0;
|
|
19816
19826
|
if (valueAxis?.crossesAt !== undefined || valueAxis?.crosses === 'autoZero') {
|
|
19817
|
-
return valueToY(clamp$
|
|
19827
|
+
return valueToY(clamp$6(crossingValue, range.min, range.max), range, layout.plotTop, layout.plotBottom);
|
|
19818
19828
|
}
|
|
19819
19829
|
return fallback === 'top' ? layout.plotTop : layout.plotBottom;
|
|
19820
19830
|
}
|
|
@@ -19830,10 +19840,10 @@ function verticalAxisX(categoryAxis, categoryCount, layout, fallback, dateValues
|
|
|
19830
19840
|
if (categoryAxis.axisType === 'dateAx' && dateValues?.length) {
|
|
19831
19841
|
const min = categoryAxis.min ?? Math.min(...dateValues);
|
|
19832
19842
|
const max = categoryAxis.max ?? Math.max(...dateValues);
|
|
19833
|
-
const ratio = max === min ? 0.5 : (clamp$
|
|
19843
|
+
const ratio = max === min ? 0.5 : (clamp$6(categoryAxis.crossesAt, min, max) - min) / (max - min);
|
|
19834
19844
|
return layout.plotLeft + ratio * layout.plotWidth;
|
|
19835
19845
|
}
|
|
19836
|
-
const index = clamp$
|
|
19846
|
+
const index = clamp$6(categoryAxis.crossesAt - 1, 0, Math.max(categoryCount - 1, 0));
|
|
19837
19847
|
const ratio = categoryCount <= 1 ? 0.5 : index / (categoryCount - 1);
|
|
19838
19848
|
return layout.plotLeft + ratio * layout.plotWidth;
|
|
19839
19849
|
}
|
|
@@ -19948,7 +19958,7 @@ function buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, cat
|
|
|
19948
19958
|
}
|
|
19949
19959
|
|
|
19950
19960
|
/** Per-category absolute totals (for percentStacked normalisation). */
|
|
19951
|
-
function categoryTotals$
|
|
19961
|
+
function categoryTotals$1(series, catCount) {
|
|
19952
19962
|
return Array.from({ length: catCount }, (_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0));
|
|
19953
19963
|
}
|
|
19954
19964
|
/**
|
|
@@ -19961,7 +19971,7 @@ function buildPercentStackedBars(chartData, catCount, layout, primaryRange, sour
|
|
|
19961
19971
|
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
19972
|
...entry,
|
|
19963
19973
|
values: sourceIndices.map((sourceIndex) => entry.values[sourceIndex] ?? 0),
|
|
19964
|
-
})), totals = categoryTotals$
|
|
19974
|
+
})), totals = categoryTotals$1(displaySeries, catCount);
|
|
19965
19975
|
for (let ci = 0; ci < catCount; ci++) {
|
|
19966
19976
|
let posRunning = 0, negRunning = 0;
|
|
19967
19977
|
const catTotal = totals[ci] || 1;
|
|
@@ -21423,7 +21433,7 @@ function widestCategoryLabelWidth(categoryLabels, fontSize) {
|
|
|
21423
21433
|
return categoryLabels.reduce((max, label) => Math.max(max, estimateTextWidth(label, fontSize)), 0);
|
|
21424
21434
|
}
|
|
21425
21435
|
/** Per-category absolute totals (for percentStacked normalisation). */
|
|
21426
|
-
function categoryTotals
|
|
21436
|
+
function categoryTotals(series, catCount) {
|
|
21427
21437
|
return Array.from({ length: catCount }, (_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0));
|
|
21428
21438
|
}
|
|
21429
21439
|
/** Resolve the fill for one bar, honouring dPt overrides and varyColors. */
|
|
@@ -21509,7 +21519,7 @@ function buildHorizontalBarViewModel(element, chartData, categoryLabels) {
|
|
|
21509
21519
|
}
|
|
21510
21520
|
}
|
|
21511
21521
|
else {
|
|
21512
|
-
const totals = isPercent ? categoryTotals
|
|
21522
|
+
const totals = isPercent ? categoryTotals(series, catCount) : [], barH = band * (isPercent ? 0.6 : 0.7), barOffset = (band - barH) / 2;
|
|
21513
21523
|
for (let ci = 0; ci < catCount; ci++) {
|
|
21514
21524
|
let posRunning = 0, negRunning = 0;
|
|
21515
21525
|
const catTotal = isPercent ? totals[ci] || 1 : 1;
|
|
@@ -26429,7 +26439,7 @@ const RADAR_RINGS = 4, RADAR_RING_COLOR = '#cbd5e1', RADAR_SPOKE_COLOR = '#94a3b
|
|
|
26429
26439
|
// COM (callouts-com.pptx): with no c:dLblPos, PowerPoint centres a radar
|
|
26430
26440
|
// label on the spoke, this many label font sizes out past its point.
|
|
26431
26441
|
RADAR_LABEL_RADIAL_EM = 3.5;
|
|
26432
|
-
const clamp$
|
|
26442
|
+
const clamp$5 = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
|
|
26433
26443
|
/**
|
|
26434
26444
|
* Build the view-model for a radar / spider chart. Polar, so it has no
|
|
26435
26445
|
* cartesian gridlines/axes; ring + spoke geometry and the data polygons all
|
|
@@ -26525,8 +26535,8 @@ function buildRadarViewModel(element, chartData, categoryLabels) {
|
|
|
26525
26535
|
const angle = radarAngle(vi, catCount), out = RADAR_LABEL_RADIAL_EM * fontSize,
|
|
26526
26536
|
// Kept inside the chart box, as PowerPoint keeps every label.
|
|
26527
26537
|
auto = {
|
|
26528
|
-
x: clamp$
|
|
26529
|
-
y: clamp$
|
|
26538
|
+
x: clamp$5(p.x + out * Math.cos(angle), fontSize, frame.width - fontSize),
|
|
26539
|
+
y: clamp$5(p.y + out * Math.sin(angle), fontSize, frame.height - fontSize),
|
|
26530
26540
|
};
|
|
26531
26541
|
anchor = {
|
|
26532
26542
|
...applyLabelManualLayout(findPointLabel(series, vi)?.layout, frame, auto),
|
|
@@ -87455,620 +87465,6 @@ function buildSmartArt3DSpecForElement(element, options = {}) {
|
|
|
87455
87465
|
return model.meshes.length > 0 ? model : null;
|
|
87456
87466
|
}
|
|
87457
87467
|
|
|
87458
|
-
/**
|
|
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).
|
|
87738
|
-
*
|
|
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.
|
|
87818
|
-
*
|
|
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".
|
|
87823
|
-
*/
|
|
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;
|
|
87908
|
-
}
|
|
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
|
-
}
|
|
87926
|
-
}
|
|
87927
|
-
return boxes;
|
|
87928
|
-
}
|
|
87929
|
-
/**
|
|
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`).
|
|
87943
|
-
*/
|
|
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
|
-
});
|
|
87980
|
-
}
|
|
87981
|
-
}
|
|
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);
|
|
88070
|
-
}
|
|
88071
|
-
|
|
88072
87468
|
/** Value-axis label text (`percentStacked` axes read as whole percents). */
|
|
88073
87469
|
function formatValueLabel(chartData, grouping, v) {
|
|
88074
87470
|
if (grouping === 'percentStacked') {
|
|
@@ -89067,273 +88463,6 @@ function resolveChart3DProjection(chartType, view3D) {
|
|
|
89067
88463
|
};
|
|
89068
88464
|
}
|
|
89069
88465
|
|
|
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
88466
|
/** The chart types the 3D chart scene renders (`surface` covers the 2D and 3D surface charts; see below). */
|
|
89338
88467
|
const CHART_3D_TYPES = new Set([
|
|
89339
88468
|
'bar3D',
|
|
@@ -89390,6 +88519,10 @@ function buildChart3DSpecForElement(element) {
|
|
|
89390
88519
|
: chartType === 'pie3D'
|
|
89391
88520
|
? buildPieGeometry(element, vm)
|
|
89392
88521
|
: null;
|
|
88522
|
+
if (!geometry) {
|
|
88523
|
+
// Nothing to draw in 3D (no series or categories): the 2D chart stays.
|
|
88524
|
+
return null;
|
|
88525
|
+
}
|
|
89393
88526
|
const longest = chartData.series.reduce((m, series) => Math.max(m, series.values.length), 0);
|
|
89394
88527
|
const categoryLabels = chartData.categories.length > 0
|
|
89395
88528
|
? chartData.categories
|
|
@@ -89403,37 +88536,8 @@ function buildChart3DSpecForElement(element) {
|
|
|
89403
88536
|
vm,
|
|
89404
88537
|
geometry,
|
|
89405
88538
|
categoryLabels,
|
|
89406
|
-
perspective: geometry ? null : buildPerspectiveScene(element),
|
|
89407
88539
|
};
|
|
89408
88540
|
}
|
|
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
88541
|
|
|
89438
88542
|
/**
|
|
89439
88543
|
* Live height of a DOM element, for layout that has to clear a strip whose
|
|
@@ -89789,7 +88893,7 @@ const THREE_UNAVAILABLE = {
|
|
|
89789
88893
|
setInteractive: () => { },
|
|
89790
88894
|
dispose: () => { },
|
|
89791
88895
|
};
|
|
89792
|
-
const FOV
|
|
88896
|
+
const FOV = 50;
|
|
89793
88897
|
/** Dynamically load `three`; returns `null` when the package is not installed. */
|
|
89794
88898
|
async function loadThree() {
|
|
89795
88899
|
try {
|
|
@@ -89868,7 +88972,7 @@ async function mountModel3D(container, modelUrl, options) {
|
|
|
89868
88972
|
if (options.background) {
|
|
89869
88973
|
scene.background = new three.Color(options.background);
|
|
89870
88974
|
}
|
|
89871
|
-
const camera = new three.PerspectiveCamera(FOV
|
|
88975
|
+
const camera = new three.PerspectiveCamera(FOV, width / height, 0.1, 1000);
|
|
89872
88976
|
camera.position.set(0, 0, 5);
|
|
89873
88977
|
camera.lookAt(0, 0, 0);
|
|
89874
88978
|
scene.add(new three.AmbientLight(0xffffff, 0.5));
|
|
@@ -90138,1171 +89242,6 @@ function computeCameraPlacement(cols, rows, view3D) {
|
|
|
90138
89242
|
};
|
|
90139
89243
|
}
|
|
90140
89244
|
|
|
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
89245
|
/**
|
|
91307
89246
|
* Pure raycast-hit -> hover-tooltip mapping for the interactive 3D bar chart
|
|
91308
89247
|
* scene ({@link ./bar-chart-3d-scene.ts}).
|
|
@@ -91332,978 +89271,6 @@ function buildBarChart3DHoverTooltip(hit, data) {
|
|
|
91332
89271
|
return buildMarkTooltip(data.seriesNames[hit.seriesIndex], data.categoryLabels[hit.categoryIndex], hit.value, data.numberFormats?.[hit.seriesIndex]);
|
|
91333
89272
|
}
|
|
91334
89273
|
|
|
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
89274
|
/**
|
|
92308
89275
|
* SmartArt fallback-layout paint decisions: where a node's label sits and how a
|
|
92309
89276
|
* connector is stroked.
|
|
@@ -120270,16 +117237,6 @@ async function loadChart3DThree() {
|
|
|
120270
117237
|
return null;
|
|
120271
117238
|
}
|
|
120272
117239
|
}
|
|
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
117240
|
|
|
120284
117241
|
/** Largest backing-store edge a single view is drawn at, in device pixels. */
|
|
120285
117242
|
const MAX_VIEW_PIXELS = 4096;
|
|
@@ -120461,8 +117418,8 @@ function resetThreeRendererHostForTests() {
|
|
|
120461
117418
|
}
|
|
120462
117419
|
|
|
120463
117420
|
const loaders = {
|
|
120464
|
-
chart: async () => (await import('./pptx-angular-viewer-chart-3d-view-scene-
|
|
120465
|
-
smartart: async () => (await import('./pptx-angular-viewer-view-scene-
|
|
117421
|
+
chart: async () => (await import('./pptx-angular-viewer-chart-3d-view-scene-Cfk7dMVK.mjs')).mountChart3DView,
|
|
117422
|
+
smartart: async () => (await import('./pptx-angular-viewer-view-scene-CNlGt3WC.mjs')).mountSmartArt3DView,
|
|
120466
117423
|
};
|
|
120467
117424
|
/** Resolve the scene factory for a spec kind. */
|
|
120468
117425
|
async function loadThreeViewScene(kind) {
|
|
@@ -120624,10 +117581,6 @@ class ThreeViewController {
|
|
|
120624
117581
|
let view = null;
|
|
120625
117582
|
const scene = await factory(spec.spec, {
|
|
120626
117583
|
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
117584
|
size: this.size,
|
|
120632
117585
|
eventTarget: this.opts.eventTarget,
|
|
120633
117586
|
overlay,
|
|
@@ -130641,7 +127594,7 @@ function createLocalStorageBackend(namespace) {
|
|
|
130641
127594
|
/** Try IndexedDB first; fall back to localStorage on any failure. */
|
|
130642
127595
|
async function resolveBackend(dbName, namespace) {
|
|
130643
127596
|
try {
|
|
130644
|
-
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-
|
|
127597
|
+
const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-BnV3ANp_.mjs');
|
|
130645
127598
|
const db = await openChatDb(dbName);
|
|
130646
127599
|
return createIdbBackend(db);
|
|
130647
127600
|
}
|
|
@@ -176636,7 +173589,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
176636
173589
|
}], 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
173590
|
|
|
176638
173591
|
// Generated by scripts/inline-shared.mjs from package.json. Do not edit.
|
|
176639
|
-
const PPTX_ANGULAR_VIEWER_VERSION = "4.10.
|
|
173592
|
+
const PPTX_ANGULAR_VIEWER_VERSION = "4.10.1";
|
|
176640
173593
|
|
|
176641
173594
|
/**
|
|
176642
173595
|
* account-page.component.ts: File > Account content.
|
|
@@ -214965,5 +211918,5 @@ function cn(...values) {
|
|
|
214965
211918
|
* Generated bundle index. Do not edit.
|
|
214966
211919
|
*/
|
|
214967
211920
|
|
|
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-
|
|
211921
|
+
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 };
|
|
211922
|
+
//# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-Dy62s3Dt.mjs.map
|