@peter.prib/renderengine 0.14.0 → 0.15.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.
Files changed (109) hide show
  1. package/README.md +45 -19
  2. package/bin/renderengine.mjs +157 -0
  3. package/dist/renderengine-workspace.umd.js +49391 -39748
  4. package/dist/renderengine.umd.js +25366 -15650
  5. package/documentation/REFERENCE.md +38 -0
  6. package/package.json +6 -1
  7. package/src/CanvasRenderer.js +225 -200
  8. package/src/SVGRenderer.js +476 -454
  9. package/src/archimate.js +22 -2
  10. package/src/archimateArchiFormat.js +1735 -1701
  11. package/src/archimateArchive.js +36 -7
  12. package/src/archimateCSV.js +93 -37
  13. package/src/archimateDrawio.js +2 -0
  14. package/src/archimateExchangeFormat.js +3 -1
  15. package/src/chartCatalog.js +219 -0
  16. package/src/charting/Annotations.js +169 -0
  17. package/src/charting/ArchimateDiagramEditor.js +54 -10
  18. package/src/charting/Axis.js +217 -5
  19. package/src/charting/Chart.js +99 -9
  20. package/src/charting/Chart3D.js +3 -0
  21. package/src/charting/ChartBox.js +8 -1
  22. package/src/charting/ColorPalette.js +42 -4
  23. package/src/charting/DiagramEditor.js +174 -39
  24. package/src/charting/DiagramEditorCommands.js +28 -0
  25. package/src/charting/Edge.js +7 -0
  26. package/src/charting/Hierarchy.js +114 -0
  27. package/src/charting/InteractionManager.js +4 -0
  28. package/src/charting/ModelFileFormat.js +5 -1
  29. package/src/charting/SettingsManager.js +35 -0
  30. package/src/charting/ShapePaths.js +82 -10
  31. package/src/charting/archimate/ArchimateCsvFileFormat.js +51 -12
  32. package/src/charting/archimate/ArchimateElementTemplate.js +235 -231
  33. package/src/charting/charts/AlluvialDiagram.js +311 -0
  34. package/src/charting/charts/AreaChart.js +52 -15
  35. package/src/charting/charts/BarChart.js +38 -3
  36. package/src/charting/charts/BarChart3D.js +5 -3
  37. package/src/charting/charts/BarChartBase.js +26 -6
  38. package/src/charting/charts/BeeswarmChart.js +164 -0
  39. package/src/charting/charts/BoxPlotChart.js +23 -1
  40. package/src/charting/charts/CalendarHeatmapChart.js +2 -1
  41. package/src/charting/charts/CandlestickChart.js +172 -4
  42. package/src/charting/charts/ChordDiagram.js +105 -2
  43. package/src/charting/charts/CirclePackChart.js +81 -0
  44. package/src/charting/charts/ComboChart.js +233 -0
  45. package/src/charting/charts/ContourChart.js +310 -0
  46. package/src/charting/charts/DonutChart3D.js +13 -0
  47. package/src/charting/charts/DumbbellChart.js +217 -0
  48. package/src/charting/charts/EcdfChart.js +150 -0
  49. package/src/charting/charts/FunnelChart.js +4 -1
  50. package/src/charting/charts/GeoMapChart.js +132 -11
  51. package/src/charting/charts/GroupedBarChart.js +44 -3
  52. package/src/charting/charts/GroupedDistributionChart.js +26 -1
  53. package/src/charting/charts/HeatmapChart.js +2 -2
  54. package/src/charting/charts/HexbinChart.js +137 -0
  55. package/src/charting/charts/HierarchyChart.js +77 -3
  56. package/src/charting/charts/HorizonChart.js +105 -0
  57. package/src/charting/charts/KagiChart.js +167 -0
  58. package/src/charting/charts/LineChart.js +74 -18
  59. package/src/charting/charts/OhlcChart.js +79 -0
  60. package/src/charting/charts/ParallelCoordinatesChart.js +9 -1
  61. package/src/charting/charts/PieChart.js +195 -17
  62. package/src/charting/charts/PieChart3D.js +137 -0
  63. package/src/charting/charts/PointAndFigureChart.js +198 -0
  64. package/src/charting/charts/PolarAreaChart.js +189 -0
  65. package/src/charting/charts/RadarChart.js +7 -1
  66. package/src/charting/charts/RadialBarChart.js +5 -2
  67. package/src/charting/charts/RenkoChart.js +178 -0
  68. package/src/charting/charts/RidgelineChart.js +113 -0
  69. package/src/charting/charts/SankeyDiagram.js +127 -4
  70. package/src/charting/charts/ScatterChart.js +13 -0
  71. package/src/charting/charts/ScatterMatrixChart.js +119 -0
  72. package/src/charting/charts/SlopegraphChart.js +205 -0
  73. package/src/charting/charts/SparklineTableChart.js +166 -0
  74. package/src/charting/charts/StackedBarChart.js +48 -5
  75. package/src/charting/charts/StackedBarChart3DOrbit.js +10 -5
  76. package/src/charting/charts/StreamGraphChart.js +10 -3
  77. package/src/charting/charts/SunburstChart.js +27 -7
  78. package/src/charting/charts/SurfaceChart3D.js +157 -0
  79. package/src/charting/charts/TreemapChart.js +28 -8
  80. package/src/charting/charts/ViolinChart.js +18 -0
  81. package/src/charting/datamodel/ArchimateDataLink.js +70 -0
  82. package/src/charting/datamodel/DataLineage.js +150 -0
  83. package/src/charting/datamodel/DataModel.js +83 -1
  84. package/src/charting/datamodel/ModelMapping.js +87 -0
  85. package/src/charting/datamodel/SqlDdl.js +110 -85
  86. package/src/charting/datamodel/index.js +3 -0
  87. package/src/charting/decision/DecisionWorkbench.js +295 -0
  88. package/src/charting/diagrams/ArchimateDiagram.js +25 -9
  89. package/src/charting/diagrams/ArchimateModel.js +1382 -1238
  90. package/src/charting/diagrams/GraphDiagram.js +1424 -1350
  91. package/src/charting/diagrams/SketchView.js +121 -0
  92. package/src/charting/diagrams/sketchBoxKinds.js +30 -0
  93. package/src/charting/modelTree/AnalysisPanel.js +101 -23
  94. package/src/charting/modelTree/NavigatorModel.js +410 -0
  95. package/src/charting/modelTree/NavigatorPanel.js +453 -0
  96. package/src/charting/pivot/PivotChart.js +109 -4
  97. package/src/charting/pivot/PivotGridChart.js +321 -33
  98. package/src/charting/pivot/PivotView.js +82 -0
  99. package/src/charting/report/TemplatedReport.js +547 -0
  100. package/src/charting/templates/BoxTemplate.js +28 -27
  101. package/src/charting/templates/PackageTemplate.js +26 -25
  102. package/src/charting/templates/SwimlaneTemplate.js +19 -18
  103. package/src/charting/templates/Template.js +219 -212
  104. package/src/cli.js +146 -0
  105. package/src/index.js +3370 -2995
  106. package/src/workspace/Workspace.js +126 -7
  107. package/src/workspace/archimateServices.js +4 -0
  108. package/src/workspace/parts/ArchimateCanvasPart.js +1 -1
  109. package/src/workspace/styles.js +36 -1
@@ -53,6 +53,7 @@ export const TREE_SETTINGS_PROPERTIES = Object.freeze([
53
53
  export const TOOL_SETTINGS_DEFAULTS = Object.freeze({
54
54
  theme: 'light',
55
55
  defaultZoom: 1,
56
+ elementColors: {},
56
57
  ...Object.fromEntries(TREE_SETTINGS_PROPERTIES.map(p => [p.key, p.default])),
57
58
  });
58
59
 
@@ -159,6 +160,39 @@ export class SettingsManager {
159
160
  this._notifyListeners({ type: 'view', viewId: view.id, settings: view._settings });
160
161
  }
161
162
 
163
+ /**
164
+ * Get configured element color for an element type, resolving model -> tool -> null.
165
+ */
166
+ getElementColor(typeName, modelId = null) {
167
+ if (modelId) {
168
+ const modelColors = this.getModelOverrides(modelId).elementColors;
169
+ if (modelColors?.[typeName]) return modelColors[typeName];
170
+ }
171
+ return this.getToolSettings().elementColors?.[typeName] ?? null;
172
+ }
173
+
174
+ /**
175
+ * Set type-level color override at tool level (default) or model level.
176
+ */
177
+ setElementColor(typeName, color, { modelId = null } = {}) {
178
+ if (modelId) {
179
+ const existing = this.getModelOverrides(modelId).elementColors ?? {};
180
+ this.saveModelSettings(modelId, { elementColors: { ...existing, [typeName]: color } });
181
+ } else {
182
+ const existing = this.getToolSettings().elementColors ?? {};
183
+ this.saveToolSettings({ elementColors: { ...existing, [typeName]: color } });
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Merged element color overrides for a model (model overrides tool).
189
+ */
190
+ elementColors(modelId = null) {
191
+ const toolColors = this.getToolSettings().elementColors ?? {};
192
+ const modelColors = modelId ? (this.getModelOverrides(modelId).elementColors ?? {}) : {};
193
+ return { ...toolColors, ...modelColors };
194
+ }
195
+
162
196
  // ── Resolve Settings (hierarchy: tool ← model ← view) ──
163
197
 
164
198
  /** The cascade for one view: its own settings, then the model's, then the tool's, then defaults. */
@@ -185,6 +219,7 @@ export class SettingsManager {
185
219
  titlePosition: viewSettings.titlePosition,
186
220
  background: viewSettings.background,
187
221
  backgroundImage: viewSettings.backgroundImage,
222
+ elementColors: this.elementColors(modelId),
188
223
  };
189
224
  }
190
225
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export class ShapePaths {
5
5
  /**
6
- * Build an SVG path for an annular pie/doughnut slice.
6
+ * Build an SVG path for an annular pie/doughnut slice (circular).
7
7
  * @param {number} cx center x
8
8
  * @param {number} cy center y
9
9
  * @param {number} rOuter outer radius
@@ -13,15 +13,87 @@ export class ShapePaths {
13
13
  * @returns {string}
14
14
  */
15
15
  static donutSlicePath(cx, cy, rOuter, rInner, startAngle, endAngle) {
16
- const largeArc = (endAngle - startAngle) > Math.PI ? 1 : 0;
17
- const outerStart = { x: cx + rOuter * Math.cos(startAngle), y: cy + rOuter * Math.sin(startAngle) };
18
- const outerEnd = { x: cx + rOuter * Math.cos(endAngle), y: cy + rOuter * Math.sin(endAngle) };
19
- if (rInner <= 0) {
20
- return `M${cx},${cy} L${outerStart.x},${outerStart.y} A${rOuter},${rOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} Z`;
16
+ return ShapePaths.ellipticalSlicePath(cx, cy, rOuter, rOuter, rInner, rInner, startAngle, endAngle);
17
+ }
18
+
19
+ /**
20
+ * Build an SVG path for a circular or tilted-elliptical pie/doughnut slice.
21
+ * Handles full-circle (`2π`) spans cleanly by clamping slightly below `2π` so SVG arc
22
+ * start and end points do not coincide.
23
+ * @param {number} cx
24
+ * @param {number} cy
25
+ * @param {number} rxOuter
26
+ * @param {number} ryOuter
27
+ * @param {number} rxInner
28
+ * @param {number} ryInner
29
+ * @param {number} startAngle
30
+ * @param {number} endAngle
31
+ * @returns {string}
32
+ */
33
+ static ellipticalSlicePath(cx, cy, rxOuter, ryOuter, rxInner, ryInner, startAngle, endAngle) {
34
+ const span = endAngle - startAngle;
35
+ const effectiveEnd = span >= Math.PI * 2 - 1e-5 ? startAngle + Math.PI * 2 - 1e-4 : endAngle;
36
+ const largeArc = (effectiveEnd - startAngle) > Math.PI ? 1 : 0;
37
+ const outerStart = { x: cx + rxOuter * Math.cos(startAngle), y: cy + ryOuter * Math.sin(startAngle) };
38
+ const outerEnd = { x: cx + rxOuter * Math.cos(effectiveEnd), y: cy + ryOuter * Math.sin(effectiveEnd) };
39
+ if (rxInner <= 0 || ryInner <= 0) {
40
+ return `M${cx},${cy} L${outerStart.x},${outerStart.y} A${rxOuter},${ryOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} Z`;
41
+ }
42
+ const innerStart = { x: cx + rxInner * Math.cos(effectiveEnd), y: cy + ryInner * Math.sin(effectiveEnd) };
43
+ const innerEnd = { x: cx + rxInner * Math.cos(startAngle), y: cy + ryInner * Math.sin(startAngle) };
44
+ return `M${outerStart.x},${outerStart.y} A${rxOuter},${ryOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} `
45
+ + `L${innerStart.x},${innerStart.y} A${rxInner},${ryInner} 0 ${largeArc} 0 ${innerEnd.x},${innerEnd.y} Z`;
46
+ }
47
+
48
+ /**
49
+ * Build an SVG path for a vertical extruded cylinder skirt along `[startAngle, endAngle]`
50
+ * between `(cx, cy)` and `(cx, cy + depth)`.
51
+ */
52
+ static cylinderSkirtPath(cx, cy, rx, ry, depth, startAngle, endAngle) {
53
+ const span = endAngle - startAngle;
54
+ if (span <= 1e-6 || depth <= 0) return '';
55
+ const effectiveEnd = span >= Math.PI * 2 - 1e-5 ? startAngle + Math.PI * 2 - 1e-4 : endAngle;
56
+ const largeArc = (effectiveEnd - startAngle) > Math.PI ? 1 : 0;
57
+ const x0 = cx + rx * Math.cos(startAngle);
58
+ const y0 = cy + ry * Math.sin(startAngle);
59
+ const x1 = cx + rx * Math.cos(effectiveEnd);
60
+ const y1 = cy + ry * Math.sin(effectiveEnd);
61
+ return `M${x0},${y0} A${rx},${ry} 0 ${largeArc} 1 ${x1},${y1} `
62
+ + `L${x1},${y1 + depth} A${rx},${ry} 0 ${largeArc} 0 ${x0},${y0 + depth} Z`;
63
+ }
64
+
65
+ /**
66
+ * Build an SVG path for a flat vertical radial cut wall at `angle` from inner to outer radius
67
+ * and extruded down by `depth`.
68
+ */
69
+ static radialWallPath(cx, cy, rxInner, ryInner, rxOuter, ryOuter, depth, angle) {
70
+ const cos = Math.cos(angle);
71
+ const sin = Math.sin(angle);
72
+ const xIn = cx + rxInner * cos;
73
+ const yIn = cy + ryInner * sin;
74
+ const xOut = cx + rxOuter * cos;
75
+ const yOut = cy + ryOuter * sin;
76
+ return `M${xIn},${yIn} L${xOut},${yOut} L${xOut},${yOut + depth} L${xIn},${yIn + depth} Z`;
77
+ }
78
+
79
+ /**
80
+ * Intersect `[startAngle, endAngle]` with the angular intervals where `sin(θ) >= 0` (front outer rim)
81
+ * or `sin(θ) <= 0` (back inner rim), handling wrap-around across `[-π, 3π]`.
82
+ * @param {number} startAngle
83
+ * @param {number} endAngle
84
+ * @param {'front'|'back'} hemisphere `'front'` = `sin(θ) >= 0` (`[0, π]`), `'back'` = `sin(θ) <= 0` (`[-π, 0]`)
85
+ * @returns {Array<[number, number]>}
86
+ */
87
+ static visibleArcIntervals(startAngle, endAngle, hemisphere = 'front') {
88
+ const intervals = [];
89
+ const windows = hemisphere === 'front'
90
+ ? [[-Math.PI * 2, -Math.PI], [0, Math.PI], [Math.PI * 2, Math.PI * 3]]
91
+ : [[-Math.PI * 3, -Math.PI * 2], [-Math.PI, 0], [Math.PI, Math.PI * 2]];
92
+ for (const [w0, w1] of windows) {
93
+ const a = Math.max(startAngle, w0);
94
+ const b = Math.min(endAngle, w1);
95
+ if (b - a > 1e-5) intervals.push([a, b]);
21
96
  }
22
- const innerStart = { x: cx + rInner * Math.cos(endAngle), y: cy + rInner * Math.sin(endAngle) };
23
- const innerEnd = { x: cx + rInner * Math.cos(startAngle), y: cy + rInner * Math.sin(startAngle) };
24
- return `M${outerStart.x},${outerStart.y} A${rOuter},${rOuter} 0 ${largeArc} 1 ${outerEnd.x},${outerEnd.y} `
25
- + `L${innerStart.x},${innerStart.y} A${rInner},${rInner} 0 ${largeArc} 0 ${innerEnd.x},${innerEnd.y} Z`;
97
+ return intervals;
26
98
  }
27
99
  }
@@ -2,14 +2,15 @@ import { ModelFileFormat } from '../ModelFileFormat.js';
2
2
  import { ArchimateModel } from '../diagrams/ArchimateModel.js';
3
3
  import { exportModelCSV, importModelCSV } from '../../archimateCSV.js';
4
4
 
5
- /** Where each table starts in the one file: elements, then relationships. */
6
- const ELEMENTS_HEADER = '=== elements.csv ===\n';
7
- const RELATIONSHIPS_HEADER = '=== relationships.csv ===\n';
5
+ /** Table segment headers in the combined CSV file. */
6
+ const ELEMENTS_HEADER = '=== elements.csv ===';
7
+ const RELATIONS_HEADER = '=== relations.csv ===';
8
+ const RELATIONSHIPS_HEADER = '=== relationships.csv ===';
9
+ const PROPERTIES_HEADER = '=== properties.csv ===';
8
10
 
9
11
  /**
10
- * The model's elements and relationships as CSV, both tables in one file
11
- * (exportModelCSV's columns). A CSV holds no views: import gives a model of
12
- * its elements and relationships with one view of them all, laid out.
12
+ * The model's elements, relationships, and properties as CSV, all tables in one file.
13
+ * Compatible with both renderengine format and Archi CSV export/import.
13
14
  */
14
15
  export class ArchimateCsvFileFormat extends ModelFileFormat {
15
16
  get label() { return 'CSV'; }
@@ -18,16 +19,54 @@ export class ArchimateCsvFileFormat extends ModelFileFormat {
18
19
  get featureName() { return 'CSV'; }
19
20
 
20
21
  export(model) {
21
- const { elementsCSV, relationshipsCSV } = exportModelCSV({ elements: model.elements(), relationships: model.relationships() });
22
- return { data: `${ELEMENTS_HEADER}${elementsCSV}\n\n${RELATIONSHIPS_HEADER}${relationshipsCSV}`, mimeType: 'text/csv' };
22
+ const { elementsCSV, relationshipsCSV, propertiesCSV } = exportModelCSV({
23
+ elements: model.elements(),
24
+ relationships: model.relationships(),
25
+ });
26
+ const parts = [
27
+ `${ELEMENTS_HEADER}\n${elementsCSV}`,
28
+ `${RELATIONSHIPS_HEADER}\n${relationshipsCSV}`,
29
+ ];
30
+ if (propertiesCSV && propertiesCSV.trim().split('\n').length > 1) {
31
+ parts.push(`${PROPERTIES_HEADER}\n${propertiesCSV}`);
32
+ }
33
+ return { data: parts.join('\n\n'), mimeType: 'text/csv' };
23
34
  }
24
35
 
25
36
  import(text) {
26
37
  const normal = String(text).replace(/\r\n?/g, '\n');
27
- const at = normal.indexOf(RELATIONSHIPS_HEADER);
28
- const elementsCSV = (at < 0 ? normal : normal.slice(0, at)).replace(ELEMENTS_HEADER, '').trim();
29
- const relationshipsCSV = at < 0 ? '' : normal.slice(at + RELATIONSHIPS_HEADER.length).trim();
30
- const { elements, relationships } = importModelCSV(elementsCSV, relationshipsCSV);
38
+
39
+ let elementsCSV = '';
40
+ let relationshipsCSV = '';
41
+ let propertiesCSV = '';
42
+
43
+ // Check if file uses section markers
44
+ const hasSections = normal.includes(ELEMENTS_HEADER)
45
+ || normal.includes(RELATIONS_HEADER)
46
+ || normal.includes(RELATIONSHIPS_HEADER);
47
+
48
+ if (!hasSections) {
49
+ // Single table: assume elements.csv
50
+ elementsCSV = normal.trim();
51
+ } else {
52
+ // Split by section markers
53
+ const sections = normal.split(/(?:^|\n)(?====\s*[\w\.-]+\s*===)/g);
54
+ for (const sec of sections) {
55
+ const trimmed = sec.trim();
56
+ if (!trimmed) continue;
57
+ if (trimmed.startsWith(ELEMENTS_HEADER)) {
58
+ elementsCSV = trimmed.slice(ELEMENTS_HEADER.length).trim();
59
+ } else if (trimmed.startsWith(RELATIONS_HEADER)) {
60
+ relationshipsCSV = trimmed.slice(RELATIONS_HEADER.length).trim();
61
+ } else if (trimmed.startsWith(RELATIONSHIPS_HEADER)) {
62
+ relationshipsCSV = trimmed.slice(RELATIONSHIPS_HEADER.length).trim();
63
+ } else if (trimmed.startsWith(PROPERTIES_HEADER)) {
64
+ propertiesCSV = trimmed.slice(PROPERTIES_HEADER.length).trim();
65
+ }
66
+ }
67
+ }
68
+
69
+ const { elements, relationships } = importModelCSV(elementsCSV, relationshipsCSV, propertiesCSV);
31
70
  const model = new ArchimateModel({ elements, relationships, views: [] });
32
71
  if (elements.length) model.generateView(elements.map(e => e.id), { name: 'Imported elements' });
33
72
  return model;