@peter.prib/renderengine 0.15.1 → 0.15.3

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.
Files changed (52) hide show
  1. package/dist/renderengine-workspace.umd.js +6485 -3992
  2. package/dist/renderengine.umd.js +6251 -3877
  3. package/documentation/REFERENCE.md +384 -28
  4. package/package.json +1 -1
  5. package/src/archimateArchiFormat.js +114 -10
  6. package/src/archimateArchive.js +27 -4
  7. package/src/archimateExchangeFormat.js +12 -4
  8. package/src/archimateSpecializations.js +11 -1
  9. package/src/chartCatalog.js +6 -4
  10. package/src/charting/ArchimateDiagramEditor.js +7 -109
  11. package/src/charting/DiagramTypeRegistry.js +20 -1
  12. package/src/charting/DiagramViewPropertiesDialog.js +54 -10
  13. package/src/charting/Edge.js +22 -0
  14. package/src/charting/Legend.js +1 -2
  15. package/src/charting/Statistics.js +14 -0
  16. package/src/charting/archimate/ArchimateCanvasEditor.js +21 -5
  17. package/src/charting/archimate/ViewGroupTemplate.js +5 -2
  18. package/src/charting/charts/BarChart.js +59 -8
  19. package/src/charting/charts/HexbinChart.js +15 -0
  20. package/src/charting/charts/LollipopChart.js +42 -7
  21. package/src/charting/decision/DecisionWorkbench.js +71 -4
  22. package/src/charting/diagramModel/DiagramJsonFileFormat.js +32 -0
  23. package/src/charting/diagramModel/DiagramModel.js +39 -0
  24. package/src/charting/diagramModel/DiagramView.js +102 -61
  25. package/src/charting/diagramModel/index.js +11 -10
  26. package/src/charting/diagrams/AddGroupToViewCommand.js +35 -0
  27. package/src/charting/diagrams/ArchimateModel.js +53 -13
  28. package/src/charting/diagrams/GraphDiagram3D.js +15 -0
  29. package/src/charting/diagrams/NestInstanceCommand.js +2 -1
  30. package/src/charting/graph/GraphSource.js +72 -0
  31. package/src/charting/modelTree/NavigatorModel.js +22 -8
  32. package/src/charting/report/TemplatedReport.js +27 -377
  33. package/src/charting/report/ViewsReport.js +118 -0
  34. package/src/charting/report/reportWriters.js +496 -0
  35. package/src/charting/templates/C4BoundaryTemplate.js +34 -0
  36. package/src/charting/templates/C4ComponentTemplate.js +30 -0
  37. package/src/charting/templates/C4ContainerTemplate.js +49 -0
  38. package/src/charting/templates/C4PersonTemplate.js +35 -0
  39. package/src/charting/templates/C4SystemTemplate.js +32 -0
  40. package/src/charting/templates/C4Template.js +95 -0
  41. package/src/charting/templates/index.js +6 -0
  42. package/src/charting/templates/templateRegistry.js +77 -0
  43. package/src/compute/ComputeBackend.js +132 -0
  44. package/src/compute/CpuBackend.js +209 -0
  45. package/src/compute/WebGpuBackend.js +630 -0
  46. package/src/compute/index.js +5 -0
  47. package/src/compute/selectBackend.js +85 -0
  48. package/src/index.js +3403 -3370
  49. package/src/kernelExpr.js +80 -5
  50. package/src/workspace/parts/ArchimateEditorPart.js +2 -2
  51. package/src/workspace/parts/DiagramEditorPart.js +146 -131
  52. package/src/workspace/parts/ViewsPart.js +106 -0
@@ -104,6 +104,31 @@ catalog `key` for you.
104
104
  | `wordCloud` | `wordCloud(data, opts)` | `Array<{text: string, value: number}>` | spiral placement + bounding-box collision; `minFontSize`, `maxFontSize`, `padding` |
105
105
  | `parallelCoordinates` | `parallelCoordinates(data, opts)` | `Array<{label?, category?, [dim: number]}>` | one vertical axis per numeric dimension; category-driven colors; `dimensions` |
106
106
  | `geoMap` | `geoMap(data, opts)` | `{features: Array<{id, name, value?, color?, geometry}>, bubbles?: Array<{lat, lon, value}>}` | equirectangular projection; `center`, `scale`, `colorScale`; see note below |
107
+ | `polarArea` | `polarAreaChart(data, opts)` | `Array<{label, value, exploded?}>` | Nightingale rose equal-angle wedges; `scale: 'radius'|'area'`, `extrude3D`, `depth`, `innerRadiusRatio` |
108
+ | `pie3D` | `pieChart3D(data, opts)` | `Array<{label, value}>` | isometric 3D pie; `depth`, `tilt`, `exploded`, `colors` |
109
+ | `donut3D` | `donutChart3D(data, opts)` | `Array<{label, value}>` | isometric 3D donut; `innerRadiusRatio`, `depth`, `tilt` |
110
+ | `pareto` | `paretoChart(data, opts)` | `Array<{label, value}>` | sorted descending bars with cumulative percentage line (80/20 rule); `barColor`, `lineColor` |
111
+ | `lollipop` | `lollipopChart(data, opts)` | `Array<{label, value}>` | stem with circular point markers; `orientation: 'vertical'|'horizontal'`, `pointRadius` |
112
+ | `marimekko` | `marimekkoChart(data, opts)` | `Array<{name, values: Array<{label, value}>}>` | 2D mosaic chart where both bar width and height encode values |
113
+ | `icicle` | `icicleChart(data, opts)` | `Array<{label, value?, children?}>` | hierarchical partition chart laying out tree levels as rectangular blocks; `orientation` |
114
+ | `dumbbell` | `dumbbellChart(data, opts)` | `Array<{label, start, end}>` or multi-series `Array<{name, values}>` | connected pair of dots showing change across categories; `colorByDelta`, `showDeltas`, `orientation` |
115
+ | `slopegraph` | `slopegraph(data, opts)` | `Array<{label, values: number[]}>` or `Array<{label, start, end}>` | connects categories across stages with direct labels; `stageLabels`, `collisionAvoidance` |
116
+ | `circlePack` | `circlePackChart(data, opts)` | `Array<{label, value?, children?}>` | nested tangent circles sized by value with translucent branch containment; `padding` |
117
+ | `ridgeline` | `ridgelineChart(data, opts)` | `Array<{label, values: number[]}>` | vertically staggered, overlapping Gaussian-KDE density curves (Joy plot); `overlap`, `showMedian` |
118
+ | `beeswarm` | `beeswarmChart(data, opts)` | `Array<{label, values: number[]}>` | packed individual sample points along category slots; `mode: 'beeswarm'|'raincloud'`, `radius` |
119
+ | `combo` | `comboChart(data, opts)` | multi-series with type specifications | composite multi-series chart combining bars, lines, and area layers on primary/secondary axes |
120
+ | `hexbin` | `hexbinChart(data, opts)` | `Array<{x, y, value?}>` or `Array<[x, y]>` | 2D hexagonal density binning for large scatter clouds; `radius`, `minCount`, `colorScale` |
121
+ | `ecdf` | `ecdfChart(data, opts)` | `number[]` or `Array<{label, values: number[]}>` | empirical cumulative distribution step function (0..1); `showMedian`, `showQuantiles`, `step` |
122
+ | `splom` | `scatterMatrixChart(data, opts)` | `Array<object>` with numeric dimensions | scatter plot matrix of pairwise bivariate relationships; `dimensions`, `colorCol` |
123
+ | `sparklineTable` | `sparklineTableChart(data, opts)` | `Array<{label, values: number[], current?, change?}>` | data table with embedded inline trendline sparklines; `columns` |
124
+ | `horizon` | `horizonChart(data, opts)` | `Array<{label, values: number[]}>` | compact layered area strips folding positive and negative magnitude bands; `bands`, `posColors` |
125
+ | `ohlc` | `ohlcChart(data, opts)` | `Array<{label, open, high, low, close}>` | Open-High-Low-Close financial bar chart; `upColor`, `downColor` |
126
+ | `alluvial` | `alluvialDiagram(data, opts)` | `{dimensions: string[], rows: Array<object>}` or `Array<{steps, value}>` | multi-stage categorical flow diagram across ordered dimensions; `nodeWidth`, `curvature` |
127
+ | `renko` | `renkoChart(data, opts)` | `number[]` or `Array<{label, close}>` | brick-based trend-filtering chart filtering out time noise; `brickSize` |
128
+ | `contour` | `contourChart(data, opts)` | `number[][]` (2D scalar grid) or `Array<{x, y, value}>` | marching-squares isolines and filled isoband contours; `levels`, `filled`, `showLines` |
129
+ | `kagi` | `kagiChart(data, opts)` | `number[]` or `Array<{label, close}>` | trend reversal chart with alternating line thickness; `reversalAmount` |
130
+ | `pointAndFigure` | `pointAndFigureChart(data, opts)` | `number[]` or `Array<{label, close}>` | columns of X's (rising) and O's (falling); `boxSize`, `reversal` |
131
+ | `surface3D` | `surfaceChart3D(data, opts)` | `Array<{x, z, value}>` or `{xLabels, zLabels, values}` | continuous 3D surface mesh with gradient height coloring, wireframe, and orbit camera |
107
132
 
108
133
  **Gauges.** Every gauge is a `GaugeChart` (`src/charting/charts/GaugeChart.js`) and takes the same options:
109
134
 
@@ -1340,6 +1365,278 @@ SVG each mark answers a click with `{ label, series }`.
1340
1365
  engine.barChart(data, { onDatumClick: ({ label }) => openDetail(label) });
1341
1366
  ```
1342
1367
 
1368
+ ## Advanced Statistical, Flow, Distribution & 3D Charts
1369
+
1370
+ Specialized charts designed for dense distributions, multi-stage transitions, continuous terrains, and flow modeling.
1371
+
1372
+ ### Dumbbell / Gap Chart — `dumbbellChart(data, options)`
1373
+
1374
+ A connected pair of dots per category showing before/after, actual/target, or interval comparisons without the visual clutter of clustered bars.
1375
+
1376
+ - **Data shapes:**
1377
+ - `Array<{label: string, start: number, end: number}>`
1378
+ - Multi-series `Array<{name: string, values: Array<{label: string, value: number}>}>` (first two series used as start and end)
1379
+ - **Notable options:**
1380
+ - `orientation`: `'horizontal'` (default) or `'vertical'`.
1381
+ - `colorByDelta`: `true` to color connectors and endpoints by direction of change (green for gain, red for loss).
1382
+ - `startColor`, `endColor`: Custom endpoint colors when `colorByDelta` is false.
1383
+ - `upColor`, `downColor`, `neutralColor`: Colors used when `colorByDelta: true`.
1384
+ - `showDeltas`: `true` to render direct delta badges (e.g. `+12%`, `-5`) next to the endpoints.
1385
+ - `showValues`: `true` to render numeric value labels at dots.
1386
+ - `pointRadius`: Radius of endpoint circles in pixels (default `5`).
1387
+ - `startLabel`, `endLabel`: Custom legend labels for the start and end series.
1388
+
1389
+ ```javascript
1390
+ engine.dumbbellChart([
1391
+ { label: 'Latency', start: 240, end: 110 },
1392
+ { label: 'Throughput', start: 850, end: 1420 },
1393
+ { label: 'Error Rate', start: 4.2, end: 0.8 },
1394
+ ], { colorByDelta: true, showDeltas: true, showValues: true });
1395
+ ```
1396
+
1397
+ ### Slopegraph — `slopegraph(data, options)`
1398
+
1399
+ Connects categories across two or more discrete stages or time points to highlight hierarchy shifts, rate-of-change differences, and rank reversals.
1400
+
1401
+ - **Data shapes:**
1402
+ - `Array<{label: string, values: number[]}>` (multi-stage trajectory)
1403
+ - `Array<{label: string, start: number, end: number}>` (2-stage before/after)
1404
+ - **Notable options:**
1405
+ - `stageLabels`: Array of column headings for the stages (e.g. `['2024', '2025', '2026']`).
1406
+ - `showDirectLabels`: `true` (default) to label line endpoints directly, avoiding an external legend.
1407
+ - `labelSide`: `'both'` (default), `'left'`, or `'right'`.
1408
+ - `collisionAvoidance`: `true` (default) to run iterative vertical relaxation preventing overlapping text labels on crowded axes.
1409
+ - `lineWidth`: Stroke width of trajectory lines (default `2`).
1410
+ - `pointRadius`: Radius of dots at each stage (default `4`).
1411
+ - `highlightOnHover`: `true` (default) to dim competing lines on cursor hover.
1412
+
1413
+ ```javascript
1414
+ engine.slopegraph([
1415
+ { label: 'Product Alpha', values: [12, 19, 28] },
1416
+ { label: 'Product Beta', values: [24, 21, 15] },
1417
+ { label: 'Product Gamma', values: [8, 14, 22] },
1418
+ ], { stageLabels: ['Q1', 'Q2', 'Q3'], showDirectLabels: true });
1419
+ ```
1420
+
1421
+ ### Ridgeline Plot (Joy Plot) — `ridgelineChart(data, options)`
1422
+
1423
+ Vertically staggered, partially overlapping Gaussian kernel density estimates (KDE) across multiple ordered or categorical groups. Ideal for comparing distributions over time or stages in compact vertical space.
1424
+
1425
+ - **Data shape:** `Array<{label: string, values: number[]}>` (raw sample observations per group).
1426
+ - **Notable options:**
1427
+ - `overlap`: Vertical overlap ratio between adjacent ridges (default `0.65`; `0` = non-overlapping rows, `1.0` = high overlap).
1428
+ - `fillOpacity`: Opacity of the filled ridge area under each density curve (default `0.74`).
1429
+ - `showMedian`: `true` (default) to draw vertical line ticks at each group's sample median.
1430
+ - `medianColor`: Color of the median tick indicator (default `'#ffffff'`).
1431
+ - `colors`: Palette array for ridges, or auto-assigned from workspace theme.
1432
+
1433
+ ```javascript
1434
+ engine.ridgelineChart([
1435
+ { label: 'Sprint 1', values: [14, 18, 22, 25, 27, 28, 30, 35, 42] },
1436
+ { label: 'Sprint 2', values: [10, 12, 15, 18, 20, 22, 24, 26, 29] },
1437
+ { label: 'Sprint 3', values: [8, 9, 11, 14, 16, 17, 18, 20, 22] },
1438
+ ], { overlap: 0.7, showMedian: true });
1439
+ ```
1440
+
1441
+ ### Beeswarm & Raincloud Plot — `beeswarmChart(data, options)`
1442
+
1443
+ Plots every individual observation as a non-overlapping circle packed tightly along a category axis without jitter noise. Raincloud mode pairs the packed dot cloud with a half-KDE density violin and a compact box plot.
1444
+
1445
+ - **Data shape:** `Array<{label: string, values: number[]}>` (raw sample numbers per group).
1446
+ - **Notable options:**
1447
+ - `mode`: `'beeswarm'` (default; centered packed point swarm) or `'raincloud'` (half-violin cloud + box plot + offset swarm).
1448
+ - `radius`: Radius of each observation dot (default `3.5`).
1449
+ - `padding`: Minimum pixel gap between adjacent packed points (default `1`).
1450
+ - `halfViolin`: In raincloud mode, draws upper density envelope (default `true`).
1451
+ - `boxPlot`: In raincloud mode, draws embedded quartiles and median bar (default `true`).
1452
+ - `colors`: Array of categorical colors per group.
1453
+
1454
+ ```javascript
1455
+ engine.beeswarmChart([
1456
+ { label: 'Control', values: [2.1, 2.4, 2.5, 2.7, 2.9, 3.1, 3.2, 3.6, 4.0] },
1457
+ { label: 'Treatment', values: [3.2, 3.5, 3.8, 4.1, 4.2, 4.5, 4.8, 5.2, 5.9] },
1458
+ ], { mode: 'raincloud', radius: 4 });
1459
+ ```
1460
+
1461
+ ### Empirical Cumulative Distribution Function — `ecdfChart(data, options)`
1462
+
1463
+ Draws exact empirical step CDFs \(P(X \le x)\) from 0 to 1, comparing distributions without bin-width artifacts. Includes automated Kolmogorov-Smirnov distance calculations.
1464
+
1465
+ - **Data shapes:**
1466
+ - `number[]` (single sample)
1467
+ - `Array<{label: string, values: number[]}>` (multi-group comparison)
1468
+ - **Notable options:**
1469
+ - `showMedian`: `true` (default) to project dashed 50th-percentile lines to axes.
1470
+ - `showQuantiles`: `true` to annotate 25th, 50th, and 75th percentile markers.
1471
+ - `step`: Step interpolation style (`'vh'` default: vertical-then-horizontal, or `'hv'`).
1472
+ - `compare`: When two series are supplied, calculates and highlights the maximum vertical distance \(D_{KS}\) (`EcdfChart.ksDistance(s1, s2)`).
1473
+ - `colors`: Palette array for series steps.
1474
+
1475
+ ```javascript
1476
+ engine.ecdfChart([
1477
+ { label: 'Algorithm A', values: [12, 15, 18, 22, 25, 29, 34, 40] },
1478
+ { label: 'Algorithm B', values: [8, 11, 14, 16, 19, 21, 24, 28] },
1479
+ ], { showMedian: true, compare: true });
1480
+ ```
1481
+
1482
+ ### Hexagonal 2D Density Binning — `hexbinChart(data, options)`
1483
+
1484
+ Aggregates dense 2D scatter coordinate clouds into regular hexagonal tiles, preventing visual saturation and overplotting in large point datasets.
1485
+
1486
+ - **Data shapes:**
1487
+ - `Array<{x: number, y: number, value?: number}>`
1488
+ - `Array<[x: number, y: number]>`
1489
+ - **Notable options:**
1490
+ - `radius`: Outer radius of each hexagon cell in pixels (default `14`).
1491
+ - `minCount`: Minimum point count threshold required to render a cell (default `1`).
1492
+ - `colorScale`: Sequential color palette array (e.g. `['#e0f3f8', '#67a9cf', '#02818a', '#bd0026']`).
1493
+ - `title`, `xAxisLabel`, `yAxisLabel`: Chart and axis labels.
1494
+
1495
+ ```javascript
1496
+ const points = Array.from({ length: 5000 }, () => ({
1497
+ x: Math.random() * 100,
1498
+ y: Math.random() * 100,
1499
+ }));
1500
+ engine.hexbinChart(points, { radius: 12, minCount: 2 });
1501
+ ```
1502
+
1503
+ ### Horizon Chart — `horizonChart(data, options)`
1504
+
1505
+ Folds continuous time-series magnitude into 2 to 5 layered colored bands within a narrow vertical band (e.g. 30–50px), preserving high precision while displaying dozens of series simultaneously.
1506
+
1507
+ - **Data shape:** `Array<{label: string, values: number[]}>`
1508
+ - **Notable options:**
1509
+ - `bands`: Number of layered folding bands (default `3`).
1510
+ - `posColors`: Color gradient array for positive values (default 3-stage blues).
1511
+ - `negColors`: Color gradient array for negative values (default 3-stage reds).
1512
+ - `title`: Chart title.
1513
+
1514
+ ```javascript
1515
+ engine.horizonChart([
1516
+ { label: 'CPU Core 0', values: [10, 25, 45, 80, 95, 60, 40, 30] },
1517
+ { label: 'CPU Core 1', values: [-15, 20, 50, 70, 85, 45, 20, -10] },
1518
+ ], { bands: 3 });
1519
+ ```
1520
+
1521
+ ### Circle Packing — `circlePackChart(data, options)`
1522
+
1523
+ Represents hierarchical tree structures as nested tangent circles sized by value, with translucent ancestor boundary rings and hover elevation.
1524
+
1525
+ - **Data shape:** Hierarchical tree `Array<{label: string, value?: number, children?: Array}>`
1526
+ - **Notable options:**
1527
+ - `padding`: Pixel gap between tangent sibling circles (default `2`).
1528
+ - `showLabels`: `true` (default) to label circles that have sufficient interior area.
1529
+ - `minLabelRadius`: Minimum circle radius in pixels required to render text inside (default `14`).
1530
+ - `colors`: Palette array mapped across top-level tree branches.
1531
+
1532
+ ```javascript
1533
+ engine.circlePackChart([{
1534
+ label: 'Architecture',
1535
+ children: [
1536
+ { label: 'Backend', children: [{ label: 'API', value: 40 }, { label: 'DB', value: 30 }] },
1537
+ { label: 'Frontend', children: [{ label: 'Web', value: 25 }, { label: 'Mobile', value: 15 }] },
1538
+ ],
1539
+ }], { padding: 4 });
1540
+ ```
1541
+
1542
+ ### Alluvial Diagram — `alluvialDiagram(data, options)`
1543
+
1544
+ Visualizes categorical flows across three or more sequential dimensions using smooth cubic bezier ribbons whose widths represent flow volumes.
1545
+
1546
+ - **Data shapes:**
1547
+ - Multi-stage pathway list: `Array<{steps: string[], value: number}>`
1548
+ - Tabular rows: `{dimensions: string[], rows: Array<object>}`
1549
+ - Edge list: `Array<{source: string, target: string, value: number}>`
1550
+ - **Notable options:**
1551
+ - `dimensions`: Array of dimension stage names (e.g. `['Role', 'Department', 'Access Level']`).
1552
+ - `nodeWidth`: Width of category pillar rectangles in pixels (default `18`).
1553
+ - `nodePadding`: Vertical separation between category nodes (default `12`).
1554
+ - `curvature`: Bezier curvature ratio for flow links (default `0.5`).
1555
+ - `linkOpacity`: Fill opacity for flow ribbons (default `0.55`).
1556
+ - `colors`: Palette array for node categories.
1557
+
1558
+ ```javascript
1559
+ engine.alluvialDiagram([
1560
+ { steps: ['Engineering', 'Senior', 'Admin'], value: 12 },
1561
+ { steps: ['Engineering', 'Junior', 'User'], value: 28 },
1562
+ { steps: ['Product', 'Lead', 'Admin'], value: 5 },
1563
+ { steps: ['Product', 'Analyst', 'User'], value: 15 },
1564
+ ], { dimensions: ['Department', 'Seniority', 'Permission'] });
1565
+ ```
1566
+
1567
+ ### Polar Area (Nightingale Rose) — `polarAreaChart(data, options)`
1568
+
1569
+ Divides a circle into equal angular sectors where the radial distance (or area) of each sector represents its magnitude. Supports optional 3D isometric extrusion and interactive wedge pull-out.
1570
+
1571
+ - **Data shape:** `Array<{label: string, value: number, exploded?: boolean}>`
1572
+ - **Notable options:**
1573
+ - `scale`: `'radius'` (linear radial distance) or `'area'` (square-root scaling so slice area is proportional to value).
1574
+ - `extrude3D`: `true` to render as an isometric 3D cylindrical cylinder with depth shading.
1575
+ - `depth`: Extrusion height in pixels when `extrude3D: true` (default `22`).
1576
+ - `innerRadiusRatio`: Donut hole ratio (default `0.12`).
1577
+ - `padAngle`: Angular spacing between sectors in radians (default `0.02`).
1578
+ - `hoverExplode`: `true` to pull out wedges under the cursor.
1579
+
1580
+ ```javascript
1581
+ engine.polarAreaChart([
1582
+ { label: 'North', value: 45 },
1583
+ { label: 'East', value: 80 },
1584
+ { label: 'South', value: 35 },
1585
+ { label: 'West', value: 60 },
1586
+ ], { scale: 'area', extrude3D: true, depth: 25 });
1587
+ ```
1588
+
1589
+ ### 3D Surface Mesh — `surfaceChart3D(data, options)`
1590
+
1591
+ Renders continuous 3D mathematical surfaces, terrain elevations, or 2D matrix loss landscapes using true 4×4 perspective transformations and interactive orbit camera rotation.
1592
+
1593
+ - **Data shapes:**
1594
+ - Regular grid: `{xLabels: string[]|number[], zLabels: string[]|number[], values: number[][]}`
1595
+ - Coordinate tuples: `Array<{x: string|number, z: string|number, value: number}>`
1596
+ - **Notable options:**
1597
+ - `wireframe`: `true` (default) to overlay contrasting grid lines on surface polygons.
1598
+ - `wireframeColor`: Stroke color for wireframe lines (default `'rgba(255,255,255,0.45)'`).
1599
+ - `colorScale`: Array of gradient color stops for height-based surface shading.
1600
+ - `initialTheta`, `initialPhi`, `initialDistance`: Initial camera angles and zoom distance.
1601
+ - Orbit interaction: Drag to rotate camera azimuth and elevation, mouse wheel to zoom in/out.
1602
+
1603
+ ```javascript
1604
+ const xLabels = [-2, -1, 0, 1, 2];
1605
+ const zLabels = [-2, -1, 0, 1, 2];
1606
+ const values = xLabels.map(x => zLabels.map(z => Math.sin(Math.sqrt(x * x + z * z))));
1607
+
1608
+ engine.surfaceChart3D({ xLabels, zLabels, values }, {
1609
+ title: 'Bivariate Ripple Surface',
1610
+ wireframe: true,
1611
+ });
1612
+ ```
1613
+
1614
+ ### Contour Plot — `contourChart(data, options)`
1615
+
1616
+ Generates smooth isoline curves and filled isoband color regions from 2D scalar fields using the marching-squares algorithm with automated numeric elevation labels.
1617
+
1618
+ - **Data shapes:**
1619
+ - 2D scalar matrix: `number[][]` (rows × columns)
1620
+ - Coordinate list: `Array<{x: number, y: number, value: number}>`
1621
+ - **Notable options:**
1622
+ - `levels`: Number of contour threshold intervals (default `8`), or an explicit array of numbers `[0.1, 0.25, 0.5, 0.75]`.
1623
+ - `filled`: `true` (default) to render shaded polygon bands between isolines.
1624
+ - `showLines`: `true` (default) to stroke isoline boundaries.
1625
+ - `showLabels`: `true` (default) to annotate contour lines with numeric elevation values.
1626
+ - `colorScale`: Array of colors interpolated across contour levels.
1627
+ - `lineColor`: Stroke color for isolines (default `'rgba(0,0,0,0.3)'`).
1628
+
1629
+ ```javascript
1630
+ const grid = [
1631
+ [0.1, 0.2, 0.4, 0.2, 0.1],
1632
+ [0.2, 0.6, 0.9, 0.6, 0.2],
1633
+ [0.4, 0.9, 1.0, 0.9, 0.4],
1634
+ [0.2, 0.6, 0.9, 0.6, 0.2],
1635
+ [0.1, 0.2, 0.4, 0.2, 0.1],
1636
+ ];
1637
+ engine.contourChart(grid, { levels: 6, filled: true, showLabels: true });
1638
+ ```
1639
+
1343
1640
  ## Common options
1344
1641
 
1345
1642
  Not every method takes every option below — the table above calls out what's notable per type —
@@ -1523,10 +1820,10 @@ data — `addDataPoint` is a **no-op** when it's `'none'`:
1523
1820
 
1524
1821
  | `streamShape` | Chart types | Behavior |
1525
1822
  |---|---|---|
1526
- | `flat` | `bar`, `pie`, `scatter`, `bubble`, `candlestick`, `stackedBar`, `funnel`, `waterfall`, `histogram`, `qq`, `lag`, `treemap`, `sunburst`, `outline`, `bar3D`, `bubble3D` | one new record appended to the outer array |
1527
- | `series` | `line`, `area`, `pipe` | every series' own `values` grows by one point |
1528
- | `grouped-sample` | `boxPlot`, `density`, `violin` | exactly ONE randomly-chosen group's `values` grows (a new sample observation belongs to one group, unlike a shared time axis) |
1529
- | `none` | `heatmap`, `radar` (fixed matrix/axis-set), `bar3DOrbit`/`stackedBar3DOrbit` (fixed grid), `gauge-dial`/`gauge-bar`/`bullet` (a value to replace, not a window) | `addDataPoint` no-ops — for gauges, just call `dialGauge(newValue)`/`barGauge(newValue)`/`bulletGauge(newValue)` again directly |
1823
+ | `flat` | `bar`, `pie`, `scatter`, `bubble`, `candlestick`, `stackedBar`, `funnel`, `waterfall`, `histogram`, `qq`, `lag`, `treemap`, `sunburst`, `outline`, `bar3D`, `bubble3D`, `dumbbell`, `slopegraph`, `circlePack`, `horizon`, `polarArea`, `hexbin`, `pareto`, `lollipop` | one new record appended to the outer array |
1824
+ | `series` | `line`, `area`, `pipe`, `combo` | every series' own `values` grows by one point |
1825
+ | `grouped-sample` | `boxPlot`, `density`, `violin`, `ecdf`, `beeswarm`, `ridgeline` | exactly ONE randomly-chosen group's `values` grows (a new sample observation belongs to one group, unlike a shared time axis) |
1826
+ | `none` | `heatmap`, `radar` (fixed matrix/axis-set), `bar3DOrbit`/`stackedBar3DOrbit`/`surface3D` (fixed grid), `gauge-dial`/`gauge-bar`/`bullet` (a value to replace, not a window), `alluvial`, `contour`, `splom` | `addDataPoint` no-ops — for gauges, just call `dialGauge(newValue)`/`barGauge(newValue)`/`bulletGauge(newValue)` again directly |
1530
1827
 
1531
1828
  ### 2. Graphs — `addGraphNode`
1532
1829
 
@@ -2610,6 +2907,7 @@ new RectangleTemplate('r1', 'Billing', { fontFamily: 'Georgia', fontSize: '14pt'
2610
2907
  | Boxes | `RectangleTemplate`, `RoundedRectangleTemplate`, `StadiumTemplate`, `PredefinedProcessTemplate`, `ClassTemplate`, `ComponentTemplate`, `SwimlaneTemplate`, `PackageTemplate`, `BoxTemplate` (3D) |
2611
2908
  | Round | `CircleTemplate`, `EllipseTemplate`, `FinalStateTemplate`, `CloudTemplate` |
2612
2909
  | Polygons | `DiamondTemplate`, `TriangleTemplate`, `PentagonTemplate`, `HexagonTemplate`, `OctagonTemplate`, `StarTemplate`, `ParallelogramTemplate`, `TrapezoidTemplate`, `ManualInputTemplate` |
2910
+ | C4 Architecture | `C4PersonTemplate`, `C4SystemTemplate`, `C4ContainerTemplate`, `C4ComponentTemplate`, `C4BoundaryTemplate` |
2613
2911
  | Other | `CylinderTemplate`, `DocumentTemplate`, `NoteTemplate`, `DataStoreTemplate`, `ActorTemplate`, `LifelineTemplate` |
2614
2912
 
2615
2913
  A template draws its outline in `shapeElements(doc)`, or in `points()` when it extends
@@ -2642,6 +2940,19 @@ empty canvas, or on the shape at the other end, cancels. This is one undo step
2642
2940
 
2643
2941
  **Container nesting and hierarchy.** Shapes such as `PackageTemplate`, `SwimlaneTemplate`, and `BoxTemplate` are containers (`isContainer = true`). When an element is dropped inside a container (`nestsOnDrop(parentId, childId)`), it is parented via `NestElementCommand` (setting `child.parentId`). Moving a container recursively moves all nested descendants (`moversFor(templateId)`). Containers can automatically expand their bounds to fit their contents via `fitNesting(changes, { padding })`. Ancestor loops are prevented (`isAncestor(childId, parentId)`).
2644
2942
 
2943
+ **C4 Model notation & architecture diagrams.** The `'c4-architecture'` category in `DIAGRAM_TYPES` (and `editor.setPaletteCategory('c4-architecture')`) provides first-class support for Simon Brown's C4 model across Context, Container, Component, and Code levels:
2944
+ - `C4PersonTemplate`: A human user, actor, persona, or stakeholder (`#08427b`, white text) with an integrated person silhouette badge and `[Person]` tagline.
2945
+ - `C4SystemTemplate`: Software system in focus (`#1168bd`, white text, `[Software System]`) or external software systems (`external: true`, `#999999`, `[Software System: External]`).
2946
+ - `C4ContainerTemplate`: Applications, web services, or microservices (`#438dd5`, white text, `[Container: <technology>]`), and database datastores (`containerType: 'database'`, cylinder icon).
2947
+ - `C4ComponentTemplate`: Modular components, controllers, and services (`#85bbf0`, dark text, `[Component: <technology>]`) with the UML component glyph.
2948
+ - `C4BoundaryTemplate`: Enterprise, system, or container scope boundaries (`isContainer = true`, dashed border `#707070`, bracketed header `[<boundaryType> Boundary]`) that enclose nested child elements.
2949
+
2950
+ ```javascript
2951
+ const person = new C4PersonTemplate('p1', 'Customer', { description: 'Personal banking customer' });
2952
+ const system = new C4SystemTemplate('s1', 'Internet Banking System', { description: 'Allows customers to view accounts and make payments' });
2953
+ const boundary = new C4BoundaryTemplate('b1', 'Big Bank plc', { boundaryType: 'Enterprise' });
2954
+ ```
2955
+
2645
2956
  **Line style levels.** A line's style resolves through `EdgeStyleCascade`: the line itself,
2646
2957
  then each level in `editor.edgeStyleLevels` (most specific first, `[{ key, label, style }]`),
2647
2958
  then the schema default. A value a level doesn't set is inherited. `EDGE_STYLE_PROPERTIES` is the
@@ -3209,7 +3520,7 @@ drawn with this library's charts. It reads decisionengine's JSON, not decisionen
3209
3520
  document
3210
3521
 
3211
3522
  ```js
3212
- { kind: 'sadis-workbench', report, plan?, before? } // report.toJSON(), plan.toJSON(), an earlier report.toJSON()
3523
+ { kind: 'sadis-workbench', report, plan?, before?, graph? } // report.toJSON(), plan.toJSON(), an earlier report.toJSON(), the scored graph
3213
3524
  ```
3214
3525
 
3215
3526
  which decisionengine's SADIS Explorer downloads ("Download for the Decision Workbench").
@@ -3228,6 +3539,7 @@ for (const view of workbench.views()) {
3228
3539
  |---|---|---|
3229
3540
  | Executive | a report | `kpi-tile` (what to build next; half the value delivered by), `stackedBar` (score part by part), `line` (value delivered by period) |
3230
3541
  | Architecture | a report | `scatter` (visible rating against basin position: foundations sit high and left), `lollipop` (share each frees) |
3542
+ | Explore | the scored graph | `graph` diagram of the first candidate's neighbourhood: its ways up to each objective it reaches (shortest, along contributions) and what waits on it (depends on it, or is blocked by it); notes spell each way out. Walked with `NavigatorModel.forGraph` |
3231
3543
  | Delivery | a plan | `gantt` (the plan on a calendar), `stackedBar` (capacity used per period, per team when planned by team) |
3232
3544
  | Portfolio | a report with perspectives | `heatmap` (rank by perspective), `dumbbell` (best and worst rank, most contested first) |
3233
3545
  | Replay | an earlier report | `slopegraph` (rank then → now); notes say what entered, left (and why) and moved |
@@ -3385,7 +3697,7 @@ nav.back(); // returns to 'billing-app'
3385
3697
  const paths = nav.findPaths('billing-app', 'ledger-db', 5);
3386
3698
 
3387
3699
  // Trace transitive downstream or upstream impact:
3388
- const impact = nav.findImpact('billing-app', { maxDepth: 4, direction: 'outgoing' });
3700
+ const impact = nav.traceImpact('billing-app', { maxDepth: 4, direction: 'outgoing' });
3389
3701
 
3390
3702
  // 2. Build or render the Navigator UI panel:
3391
3703
  const panel = new NavigatorPanel({
@@ -3400,7 +3712,26 @@ const panel = new NavigatorPanel({
3400
3712
  - **Cycle detection and path tracking:** Tracks visited nodes along active branches to prevent infinite recursion in cyclic relationship graphs, annotating cycles in tree outputs.
3401
3713
  - **Directional filters:** Traversal can follow outgoing relationships, incoming relationships, or bidirectional connections (`setDirection('outgoing' | 'incoming' | 'both')`).
3402
3714
  - **History stack:** Maintains full forward and backward navigation stacks (`focus(id)`, `back()`, `forward()`, `canGoBack`, `canGoForward`).
3403
- - **Impact and path finding:** Built-in BFS/DFS algorithms find connecting pathways (`findPaths(fromId, toId, maxDepth)`) and downstream dependency cascades (`findImpact(elementId, { maxDepth, direction })`).
3715
+ - **Impact and path finding:** Built-in BFS/DFS algorithms find connecting pathways (`findPaths(fromId, toId, maxDepth)`) and downstream dependency cascades (`traceImpact(elementId, { maxDepth, direction })`).
3716
+
3717
+
3718
+ **Any graph, not only ArchiMate.** `NavigatorModel` reads its graph only through four questions —
3719
+ `getElement(id)`, `relationshipsFor(id)` (`{ incoming, outgoing }`, each `{ id, source, target, kind }`),
3720
+ `viewsContaining(id)` and `layerOf(element)` — the `GraphSource` interface
3721
+ (`src/charting/graph/GraphSource.js`). `ArchimateModel` answers them already (its layers from
3722
+ `treeLayerOf`); `DocumentGraph` answers them for a graph document, `{ nodes: [{ id, labels, props }],
3723
+ edges: [{ from, to, type, props }] }` — sqlengine's graphs and decisionengine's portfolios — a node's type its
3724
+ first label, its layer its type:
3725
+
3726
+ ```javascript
3727
+ import { NavigatorModel, DocumentGraph } from '@peter.prib/renderengine';
3728
+ const nav = NavigatorModel.forGraph(graphDocument, { rootId: 'cap-semantic-registry' });
3729
+ nav.findPaths('cap-semantic-registry', 'obj-trusted-data', { direction: 'outgoing', allowedKinds: ['ENABLES', 'SUPPORTS'] });
3730
+ nav.traceImpact('cap-semantic-registry', { direction: 'incoming', allowedKinds: ['DEPENDS_ON'] });
3731
+ ```
3732
+
3733
+ Why an interface rather than a second walker: traversal is the same for every graph; only these reads
3734
+ differ, so each kind of graph answers them and the one walker (paths, impact, history, the tree) serves all.
3404
3735
 
3405
3736
  ## Templated Reports (`TemplatedReport`)
3406
3737
 
@@ -3492,30 +3823,55 @@ dataModel.addArchimateLink(link);
3492
3823
 
3493
3824
  ## Multi-View Chart Dashboards (`ViewsPart` / `DecisionWorkbenchPart`)
3494
3825
 
3495
- `ViewsPart` (`src/workspace/parts/ViewsPart.js`) and `DecisionWorkbenchPart` (`src/workspace/parts/DecisionWorkbenchPart.js`) render multi-tab dashboards of coordinated charts and note cards inside the `<ws-workspace>` application shell.
3826
+ `ViewsPart` (`src/workspace/parts/ViewsPart.js`, type `views`) shows a set of chart **views** inside
3827
+ one workspace part: a tab per view, each a grid of charts with its notes underneath, and a toolbar that
3828
+ opens a file. A view is plain data:
3496
3829
 
3497
- ```javascript
3498
- // Declarative view specification for a workspace layout node:
3499
- const layout = {
3500
- type: 'part',
3501
- part: 'views',
3502
- views: [
3503
- {
3504
- id: 'executive-summary',
3505
- title: 'Executive Summary',
3506
- charts: [
3507
- { title: 'System Health', chartType: 'gauge dial', data: [{ score: 94 }], options: { min: 0, max: 100 } },
3508
- { title: 'Revenue Trend', chartType: 'area', data: [...] }
3509
- ],
3510
- notes: ['All latency thresholds met', 'Quarterly SLA target achieved']
3511
- }
3512
- ]
3513
- };
3830
+ ```js
3831
+ { id: 'summary', title: 'Summary',
3832
+ charts: [{ title, chartType, data, options? }], // chartType: a CHART_CATALOG key ('kpi-tile', 'bar', 'gantt', …)
3833
+ notes: ['plain lines the charts cannot show'] }
3514
3834
  ```
3515
3835
 
3516
- - **Self-contained workbench part:** Embeds cleanly into tabs, split panes, or WorkArea documents alongside diagram modellers and data tables without hijacking global workspace perspective state.
3517
- - **Catalog-driven composition:** Composes existing `ChartPart`s driven by `CHART_CATALOG` keys alongside `ListPart` note cards. Small gauge charts automatically tile along the top banner.
3518
- - **Decision Workbench:** Specialized `DecisionWorkbenchPart` reads SADIS analysis reports and optimization plans (`viewsFrom(doc)`), projecting trade-off curves, capability radars, and decision matrices directly into tabs.
3836
+ Like any part it is registered, added with `addParts`, and placed in a layout by its `partId`. It uses
3837
+ `ChartPart`, so it is registered with it rather than being among `BUILTIN_PARTS`:
3838
+
3839
+ ```js
3840
+ const { registerBuiltinParts, ChartPart, ViewsPart } = RenderEngineWorkspace;
3841
+ registerBuiltinParts(ws);
3842
+ ws.registerParts([ChartPart, ViewsPart]);
3843
+ ws.addParts([{
3844
+ id: 'summary', type: 'views', title: 'Summary',
3845
+ views: [{
3846
+ id: 'health', title: 'Health',
3847
+ charts: [
3848
+ { title: 'Availability (%)', chartType: 'kpi-tile', data: { value: 99.95 } },
3849
+ { title: 'Errors by service', chartType: 'bar', data: [{ label: 'api', value: 12 }, { label: 'web', value: 3 }] },
3850
+ ],
3851
+ notes: ['All latency thresholds met'],
3852
+ }],
3853
+ }]);
3854
+ ws.registerPerspective('Summary', { type: 'part', partId: 'summary' });
3855
+ ```
3856
+
3857
+ - **One part, any layout.** It sits in a tab, a split, a dashboard cell or a WorkArea document beside
3858
+ what else the host shows; it does not register perspectives of its own.
3859
+ - **Composed from existing parts.** Each chart is a `ChartPart`, the notes a `ListPart`. Gauge-like
3860
+ charts (catalog `category: 'gauge'`, e.g. `kpi-tile`, `gauge-bar`, `bullet`) share the top row as
3861
+ tiles; other charts take half a row, or the whole row when a view has one (`ViewsPart.layout(views)`).
3862
+ - **Spec:** `views`, `open` (default true: the toolbar's file opener), `empty` (the text shown with no
3863
+ views). `part.read(doc)` shows a `{ views: [...] }` document from code. The views shown are kept in
3864
+ `spec.views`, so a serialized workspace comes back showing them.
3865
+ - **Other kinds of document** are subclasses overriding `viewsFrom(doc)` (and `accept`, `controls()`,
3866
+ `controlValues()` for options their reader takes). Why a subclass and not a reader in the spec, and
3867
+ why one part rather than perspectives: `documentation/WORKSPACE_ARCHITECTURE.md` (Parts).
3868
+
3869
+ `DecisionWorkbenchPart` (`src/workspace/parts/DecisionWorkbenchPart.js`, type `decision-workbench`) is
3870
+ the first such subclass. It reads a SADIS workbench document from decisionengine
3871
+ (`{ kind: 'sadis-workbench', report, plan?, before? }`) through `DecisionWorkbench` and shows its
3872
+ **executive**, **architecture**, **delivery**, **portfolio** and **replay** views (see *Decision
3873
+ Workbench* above), with toolbar fields for the calendar the delivery plan is drawn on. Spec: `ViewsPart`'s
3874
+ plus `document`, `start`, `periodDays`. `examples/decision-workbench.html` hosts one.
3519
3875
 
3520
3876
  ## Where to go next
3521
3877
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peter.prib/renderengine",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "ArchiMate modeller, data modeller, diagram editor, OLAP cube & pivot chart studio, and 50+ type chart library for the browser, with no runtime dependencies.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",