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 CHANGED
@@ -7,6 +7,13 @@ A release listed with no entries carried no Conventional Commit in this package'
7
7
  scope: scripts/release-plan.mjs re-releases a package whenever any of its files
8
8
  change, not only on conventional ones.
9
9
 
10
+ ## [4.10.1](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@4.10.1) - 2026-09-26
11
+
12
+ ### Bug Fixes
13
+
14
+ - **shared:** Fit SmartArt light rigs' key light under a scene camera (by @claude) ([91394b0](https://github.com/ChristopherVR/pptx-viewer/commit/91394b03ee8f68536e8099e15997465b0d257a95))
15
+ - **shared:** Keep a 3D view's scene across a move and a same-task re-attach (by @claude) ([5bc411c](https://github.com/ChristopherVR/pptx-viewer/commit/5bc411cb3ba2d0613bddb678be7c5eae3972d806))
16
+
10
17
  ## [4.10.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@4.10.0) - 2026-09-26
11
18
 
12
19
  ### Features
@@ -1,4 +1,4 @@
1
- import { s as shade, c as createSvgEl, a as appendChartLegendSvg, r as renderChartLineSvg, b as renderChartTextSvg, C as CHART_3D_DRAG_THRESHOLD_PX, d as buildBarChart3DHoverTooltip, i as isChartInteractionArmed, e as roundDragValue, o as obliqueBarTaper, p as perspBoxMatrix, D as DEFAULT_PALETTE, f as buildPerspCamera, g as perspToScreen, h as pieRimPoint, j as computePieChartLayout, k as perspScreenToPlaneY, l as resolvePieSliceShareValue, m as createSurfaceChart3DScene, n as createPieChart3DScene, q as createAreaChart3DScene, u as createLineChart3DScene, v as createBarChart3DScene, w as widenPieViewModel } from './pptx-angular-viewer-pptx-angular-viewer-DvjSjGWC.mjs';
1
+ import { s as shade, c as createSvgEl, a as appendChartLegendSvg, r as renderChartLineSvg, b as renderChartTextSvg, d as buildBarChart3DHoverTooltip, i as isChartInteractionArmed, e as roundDragValue, o as obliqueBarTaper, p as perspBoxMatrix, D as DEFAULT_PALETTE, f as buildPerspCamera, g as perspToScreen, h as pieRimPoint, j as computePieChartLayout, k as perspScreenToPlaneY, l as resolvePieSliceShareValue, w as widenPieViewModel } from './pptx-angular-viewer-pptx-angular-viewer-Dy62s3Dt.mjs';
2
2
 
3
3
  /**
4
4
  * chart-3d-shading.ts: PowerPoint's flat chart-space shading for a 3D chart's
@@ -383,6 +383,70 @@ function appendBoxLabels(doc, svg, vm, labels) {
383
383
  }
384
384
  }
385
385
 
386
+ /**
387
+ * Minimum pointer travel (px) before a mark press becomes a value drag,
388
+ * mirroring {@link CHART_DRAG_THRESHOLD_PX} (`chart-canvas-drag.ts`): without
389
+ * a threshold, every click on a mark would commit a (tiny) value change, so a
390
+ * user could never select a mark without editing it.
391
+ */
392
+ const CHART_3D_DRAG_THRESHOLD_PX = 3;
393
+ /** Emissive colour + intensity applied to a selected mark's material. */
394
+ const CHART_3D_SELECTED_EMISSIVE = '#3b82f6';
395
+ const CHART_3D_SELECTED_EMISSIVE_INTENSITY = 0.55;
396
+ /**
397
+ * Map a 3D raycast hit (or none) to the same selection descriptor the 2D
398
+ * chart-interaction model uses.
399
+ */
400
+ function chart3DHitToPartRef(hit) {
401
+ if (!hit) {
402
+ return null;
403
+ }
404
+ return { role: 'dataPoint', seriesIndex: hit.seriesIndex, pointIndex: hit.pointIndex };
405
+ }
406
+ /** Whether a mesh's own (series, point) reference is the currently-selected part. */
407
+ function chart3DMarkMatchesPart(mark, part) {
408
+ return (part !== null &&
409
+ part !== undefined &&
410
+ part.role === 'dataPoint' &&
411
+ part.seriesIndex === mark.seriesIndex &&
412
+ part.pointIndex === mark.pointIndex);
413
+ }
414
+ /**
415
+ * Calibrate a value axis from two already-projected screen points known to
416
+ * correspond to two distinct data values on the same mark (e.g. its
417
+ * value-zero base and its current top). Returns `null` when the two values
418
+ * coincide or the two screen points are (near) coincident (camera looking
419
+ * straight down the axis), since no calibration is possible.
420
+ *
421
+ * Order-independent: swapping `(screenAtValue0, value0)` with
422
+ * `(screenAtValue1, value1)` yields the same {@link chart3DPointerDeltaToValueDelta}
423
+ * result, since the direction vector and `pixelsPerUnit` sign flip together.
424
+ */
425
+ function calibrateChart3DValueAxis(screenAtValue0, value0, screenAtValue1, value1) {
426
+ const valueSpan = value1 - value0;
427
+ const dx = screenAtValue1.x - screenAtValue0.x;
428
+ const dy = screenAtValue1.y - screenAtValue0.y;
429
+ const pixelSpan = Math.hypot(dx, dy);
430
+ if (valueSpan === 0 || pixelSpan < 1e-6 || !Number.isFinite(valueSpan)) {
431
+ return null;
432
+ }
433
+ return {
434
+ directionScreen: { x: dx / pixelSpan, y: dy / pixelSpan },
435
+ pixelsPerUnit: pixelSpan / valueSpan,
436
+ };
437
+ }
438
+ /**
439
+ * Convert a screen-space pointer delta (current position minus drag-start
440
+ * position, in CSS px) into a value delta, using a calibration taken at drag
441
+ * start. Add the result to the mark's value at drag start to get the live
442
+ * (or committed) dragged value.
443
+ */
444
+ function chart3DPointerDeltaToValueDelta(calibration, pointerDeltaScreen) {
445
+ const dot = pointerDeltaScreen.x * calibration.directionScreen.x +
446
+ pointerDeltaScreen.y * calibration.directionScreen.y;
447
+ return dot / calibration.pixelsPerUnit;
448
+ }
449
+
386
450
  function partOf(point) {
387
451
  return point
388
452
  ? { role: 'dataPoint', seriesIndex: point.seriesIndex, pointIndex: point.pointIndex }
@@ -683,6 +747,7 @@ function buildPerspBarPrisms(chartData, layout, override) {
683
747
  neg = to;
684
748
  }
685
749
  }
750
+ const shape = chartData.series[s]?.shape ?? chartData.barShape ?? 'box';
686
751
  const slotIndex = layout.grouping === 'clustered' ? s : 0;
687
752
  const row = layout.grouping === 'standard' ? s : 0;
688
753
  const c0 = c * slot + (gapWidth * barW) / 2 + slotIndex * barW;
@@ -705,6 +770,9 @@ function buildPerspBarPrisms(chartData, layout, override) {
705
770
  ],
706
771
  z0,
707
772
  z1: z0 + depth,
773
+ shape,
774
+ taper: obliqueBarTaper(shape, v0, v1, (layout.range.max - layout.range.min) * layout.valueScale, raw),
775
+ horizontal: layout.horizontal,
708
776
  });
709
777
  }
710
778
  }
@@ -797,6 +865,44 @@ function buildPerspPrisms(chartData, layout, override) {
797
865
  return prisms;
798
866
  }
799
867
 
868
+ /** Whether a prism is drawn as a shaped solid rather than an extrusion. */
869
+ function isShapedPerspPrism(prism) {
870
+ return prism.shape !== undefined && prism.shape !== 'box';
871
+ }
872
+ /** The prism's box-space bounding box: `[x0, x1, y0, y1]`. */
873
+ function outlineBounds(prism) {
874
+ const xs = prism.outline.map(([x]) => x);
875
+ const ys = prism.outline.map(([, y]) => y);
876
+ return [Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys)];
877
+ }
878
+ /** A shaped bar's geometry in box space, with shading baked into vertex colours. */
879
+ function buildShapedPrismGeometry(three, prism) {
880
+ const shape = prism.shape ?? 'box';
881
+ const unit = buildObliqueShapeGeometry(three, { shape, taper: prism.taper ?? { bottom: 1, top: 1 } }, Boolean(prism.horizontal));
882
+ const geometry = unit.index ? unit.toNonIndexed() : unit;
883
+ if (geometry !== unit) {
884
+ unit.dispose();
885
+ }
886
+ const [x0, x1, y0, y1] = outlineBounds(prism);
887
+ const sx = Math.max(x1 - x0, 1e-6);
888
+ const sy = Math.max(y1 - y0, 1e-6);
889
+ const sz = Math.max(prism.z1 - prism.z0, 1e-6);
890
+ geometry.scale(sx, sy, sz);
891
+ geometry.translate((x0 + x1) / 2, (y0 + y1) / 2, (prism.z0 + prism.z1) / 2);
892
+ geometry.computeVertexNormals();
893
+ const normals = geometry.getAttribute('normal');
894
+ const colors = new Float32Array(normals.count * 3);
895
+ const base = new three.Color(prism.color).getRGB(new three.Color(), three.SRGBColorSpace);
896
+ const out = new three.Color();
897
+ for (let i = 0; i < normals.count; i++) {
898
+ const f = chart3DNormalShade(shape, normals.getX(i), normals.getY(i), -normals.getZ(i));
899
+ out.setRGB(Math.min(1, base.r * f), Math.min(1, base.g * f), Math.min(1, base.b * f), three.SRGBColorSpace);
900
+ colors.set([out.r, out.g, out.b], i * 3);
901
+ }
902
+ geometry.setAttribute('color', new three.Float32BufferAttribute(colors, 3));
903
+ return geometry;
904
+ }
905
+
800
906
  /** PowerPoint's default major gridline colour (tx1 at 15% tint, `#D9D9D9`). */
801
907
  const GRIDLINE_COLOR = 0xd9d9d9;
802
908
  /** Bake face shading into a geometry's vertex colours. */
@@ -817,6 +923,9 @@ function shadePerspGeometry(three, geometry, color) {
817
923
  }
818
924
  /** An extruded, shaded prism geometry in box space. */
819
925
  function buildPrismGeometry(three, prism) {
926
+ if (isShapedPerspPrism(prism)) {
927
+ return buildShapedPrismGeometry(three, prism);
928
+ }
820
929
  const shape = new three.Shape(prism.outline.map(([x, y]) => new three.Vector2(x, y)));
821
930
  const extruded = new three.ExtrudeGeometry(shape, {
822
931
  depth: Math.max(prism.z1 - prism.z0, 1e-6),
@@ -1612,25 +1721,6 @@ function buildObliqueCamera(three, svgWidth, svgHeight, shearX, shearY) {
1612
1721
  camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert();
1613
1722
  return camera;
1614
1723
  }
1615
- /** The hosted perspective scene for a spec whose oblique geometry is `null`. */
1616
- function mountPerspectiveScene(perspective, ctx) {
1617
- switch (perspective.kind) {
1618
- case 'bar':
1619
- return createBarChart3DScene(ctx, perspective.options);
1620
- case 'line':
1621
- return createLineChart3DScene(ctx, perspective.options);
1622
- case 'area':
1623
- return createAreaChart3DScene(ctx, perspective.options);
1624
- case 'pie':
1625
- return createPieChart3DScene(ctx, perspective.options);
1626
- case 'surface':
1627
- return createSurfaceChart3DScene(ctx, perspective.options);
1628
- default: {
1629
- const exhaustive = perspective;
1630
- return exhaustive;
1631
- }
1632
- }
1633
- }
1634
1724
  async function mountChart3DView(spec, ctx) {
1635
1725
  if (spec.geometry?.kind === 'pie') {
1636
1726
  const chartData = spec.element.chartData;
@@ -1653,9 +1743,6 @@ async function mountChart3DView(spec, ctx) {
1653
1743
  return mountPerspChartView({ vm: spec.vm, layout: spec.geometry.layout, chartData, categoryLabels: spec.categoryLabels }, ctx);
1654
1744
  }
1655
1745
  if (spec.projection.mode !== 'oblique' || spec.geometry?.kind !== 'oblique') {
1656
- if (spec.perspective) {
1657
- return mountPerspectiveScene(spec.perspective, ctx);
1658
- }
1659
1746
  throw new Error(`no 3D chart scene for chartType=${spec.chartType}`);
1660
1747
  }
1661
1748
  const three = ctx.three;
@@ -1714,4 +1801,4 @@ async function mountChart3DView(spec, ctx) {
1714
1801
  }
1715
1802
 
1716
1803
  export { buildObliqueCamera, mountChart3DView };
1717
- //# sourceMappingURL=pptx-angular-viewer-chart-3d-view-scene-CreE1jB0.mjs.map
1804
+ //# sourceMappingURL=pptx-angular-viewer-chart-3d-view-scene-Cfk7dMVK.mjs.map