@peter.prib/renderengine 0.14.0 → 0.15.1
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/README.md +45 -19
- package/bin/renderengine.mjs +157 -0
- package/dist/renderengine-workspace.umd.js +49641 -39747
- package/dist/renderengine.umd.js +25366 -15650
- package/documentation/REFERENCE.md +206 -0
- package/examples/decision-workbench.html +26 -0
- package/package.json +7 -1
- package/src/CanvasRenderer.js +225 -200
- package/src/SVGRenderer.js +476 -454
- package/src/archimate.js +22 -2
- package/src/archimateArchiFormat.js +1735 -1701
- package/src/archimateArchive.js +36 -7
- package/src/archimateCSV.js +93 -37
- package/src/archimateDrawio.js +2 -0
- package/src/archimateExchangeFormat.js +3 -1
- package/src/chartCatalog.js +219 -0
- package/src/charting/Annotations.js +169 -0
- package/src/charting/ArchimateDiagramEditor.js +54 -10
- package/src/charting/Axis.js +217 -5
- package/src/charting/Chart.js +99 -9
- package/src/charting/Chart3D.js +3 -0
- package/src/charting/ChartBox.js +8 -1
- package/src/charting/ColorPalette.js +42 -4
- package/src/charting/DiagramEditor.js +174 -39
- package/src/charting/DiagramEditorCommands.js +28 -0
- package/src/charting/Edge.js +7 -0
- package/src/charting/Hierarchy.js +114 -0
- package/src/charting/InteractionManager.js +4 -0
- package/src/charting/ModelFileFormat.js +5 -1
- package/src/charting/SettingsManager.js +35 -0
- package/src/charting/ShapePaths.js +82 -10
- package/src/charting/archimate/ArchimateCsvFileFormat.js +51 -12
- package/src/charting/archimate/ArchimateElementTemplate.js +235 -231
- package/src/charting/charts/AlluvialDiagram.js +311 -0
- package/src/charting/charts/AreaChart.js +52 -15
- package/src/charting/charts/BarChart.js +38 -3
- package/src/charting/charts/BarChart3D.js +5 -3
- package/src/charting/charts/BarChartBase.js +26 -6
- package/src/charting/charts/BeeswarmChart.js +164 -0
- package/src/charting/charts/BoxPlotChart.js +23 -1
- package/src/charting/charts/CalendarHeatmapChart.js +2 -1
- package/src/charting/charts/CandlestickChart.js +172 -4
- package/src/charting/charts/ChordDiagram.js +105 -2
- package/src/charting/charts/CirclePackChart.js +81 -0
- package/src/charting/charts/ComboChart.js +233 -0
- package/src/charting/charts/ContourChart.js +310 -0
- package/src/charting/charts/DonutChart3D.js +13 -0
- package/src/charting/charts/DumbbellChart.js +217 -0
- package/src/charting/charts/EcdfChart.js +150 -0
- package/src/charting/charts/FunnelChart.js +4 -1
- package/src/charting/charts/GeoMapChart.js +132 -11
- package/src/charting/charts/GroupedBarChart.js +44 -3
- package/src/charting/charts/GroupedDistributionChart.js +26 -1
- package/src/charting/charts/HeatmapChart.js +2 -2
- package/src/charting/charts/HexbinChart.js +137 -0
- package/src/charting/charts/HierarchyChart.js +77 -3
- package/src/charting/charts/HorizonChart.js +105 -0
- package/src/charting/charts/KagiChart.js +167 -0
- package/src/charting/charts/LineChart.js +74 -18
- package/src/charting/charts/OhlcChart.js +79 -0
- package/src/charting/charts/ParallelCoordinatesChart.js +9 -1
- package/src/charting/charts/PieChart.js +195 -17
- package/src/charting/charts/PieChart3D.js +137 -0
- package/src/charting/charts/PointAndFigureChart.js +198 -0
- package/src/charting/charts/PolarAreaChart.js +189 -0
- package/src/charting/charts/RadarChart.js +7 -1
- package/src/charting/charts/RadialBarChart.js +5 -2
- package/src/charting/charts/RenkoChart.js +178 -0
- package/src/charting/charts/RidgelineChart.js +113 -0
- package/src/charting/charts/SankeyDiagram.js +127 -4
- package/src/charting/charts/ScatterChart.js +13 -0
- package/src/charting/charts/ScatterMatrixChart.js +119 -0
- package/src/charting/charts/SlopegraphChart.js +205 -0
- package/src/charting/charts/SparklineTableChart.js +166 -0
- package/src/charting/charts/StackedBarChart.js +48 -5
- package/src/charting/charts/StackedBarChart3DOrbit.js +10 -5
- package/src/charting/charts/StreamGraphChart.js +10 -3
- package/src/charting/charts/SunburstChart.js +27 -7
- package/src/charting/charts/SurfaceChart3D.js +157 -0
- package/src/charting/charts/TreemapChart.js +28 -8
- package/src/charting/charts/ViolinChart.js +18 -0
- package/src/charting/datamodel/ArchimateDataLink.js +70 -0
- package/src/charting/datamodel/DataLineage.js +150 -0
- package/src/charting/datamodel/DataModel.js +83 -1
- package/src/charting/datamodel/ModelMapping.js +87 -0
- package/src/charting/datamodel/SqlDdl.js +110 -85
- package/src/charting/datamodel/index.js +3 -0
- package/src/charting/decision/DecisionWorkbench.js +295 -0
- package/src/charting/diagrams/ArchimateDiagram.js +25 -9
- package/src/charting/diagrams/ArchimateModel.js +1382 -1238
- package/src/charting/diagrams/GraphDiagram.js +1424 -1350
- package/src/charting/diagrams/SketchView.js +121 -0
- package/src/charting/diagrams/sketchBoxKinds.js +30 -0
- package/src/charting/modelTree/AnalysisPanel.js +101 -23
- package/src/charting/modelTree/NavigatorModel.js +410 -0
- package/src/charting/modelTree/NavigatorPanel.js +453 -0
- package/src/charting/pivot/PivotChart.js +109 -4
- package/src/charting/pivot/PivotGridChart.js +321 -33
- package/src/charting/pivot/PivotView.js +82 -0
- package/src/charting/report/TemplatedReport.js +547 -0
- package/src/charting/templates/BoxTemplate.js +28 -27
- package/src/charting/templates/PackageTemplate.js +26 -25
- package/src/charting/templates/SwimlaneTemplate.js +19 -18
- package/src/charting/templates/Template.js +219 -212
- package/src/cli.js +146 -0
- package/src/index.js +3370 -2995
- package/src/workspace/Workspace.js +126 -7
- package/src/workspace/archimateServices.js +4 -0
- package/src/workspace/index.js +3 -0
- package/src/workspace/parts/ArchimateCanvasPart.js +1 -1
- package/src/workspace/parts/DecisionWorkbenchPart.js +58 -0
- package/src/workspace/parts/ViewsPart.js +196 -0
- package/src/workspace/styles.js +36 -1
package/src/CanvasRenderer.js
CHANGED
|
@@ -1,201 +1,226 @@
|
|
|
1
|
-
import { BaseRenderer } from './BaseRenderer.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @class CanvasRenderer
|
|
5
|
-
* Renders shapes by drawing on a 2D canvas context.
|
|
6
|
-
*/
|
|
7
|
-
export class CanvasRenderer extends BaseRenderer {
|
|
8
|
-
get type() { return 'canvas'; }
|
|
9
|
-
get centerTextAnchor() { return 'center'; }
|
|
10
|
-
|
|
11
|
-
constructor(container, width, height) {
|
|
12
|
-
super(container, width, height);
|
|
13
|
-
|
|
14
|
-
this.canvas
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.canvas.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return p;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.ctx.stroke
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.ctx.
|
|
84
|
-
this.ctx.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.ctx.
|
|
91
|
-
|
|
92
|
-
this.ctx.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
this.ctx.
|
|
114
|
-
this.ctx.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.
|
|
155
|
-
this.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (path2d ? this.ctx.
|
|
174
|
-
return shape;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const targetShape = this._shapes.find(s => s.id === shape.id);
|
|
191
|
-
if (targetShape) {
|
|
192
|
-
|
|
193
|
-
this._redraw();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
1
|
+
import { BaseRenderer } from './BaseRenderer.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class CanvasRenderer
|
|
5
|
+
* Renders shapes by drawing on a 2D canvas context.
|
|
6
|
+
*/
|
|
7
|
+
export class CanvasRenderer extends BaseRenderer {
|
|
8
|
+
get type() { return 'canvas'; }
|
|
9
|
+
get centerTextAnchor() { return 'center'; }
|
|
10
|
+
|
|
11
|
+
constructor(container, width, height) {
|
|
12
|
+
super(container, width, height);
|
|
13
|
+
const doc = typeof document !== 'undefined' ? document : container?.ownerDocument;
|
|
14
|
+
this.canvas = doc?.createElement ? doc.createElement('canvas') : (container?.canvas || {
|
|
15
|
+
style: {}, width, height,
|
|
16
|
+
getContext: () => ({ fillRect: () => {}, measureText: () => ({ width: 10 }), clearRect: () => {}, beginPath: () => {}, arc: () => {}, fill: () => {}, stroke: () => {} }),
|
|
17
|
+
});
|
|
18
|
+
this.canvas.width = width;
|
|
19
|
+
this.canvas.height = height;
|
|
20
|
+
// Inline elements (the default for <canvas>) leave a few px of baseline
|
|
21
|
+
// whitespace below them inside a block container, making the
|
|
22
|
+
// container's scrollHeight exceed its clientHeight for every chart.
|
|
23
|
+
if (this.canvas?.style) this.canvas.style.display = 'block';
|
|
24
|
+
this._shapes = [];
|
|
25
|
+
this.container?.appendChild?.(this.canvas);
|
|
26
|
+
this.ctx = this.canvas?.getContext?.('2d') || null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Resize the canvas. Setting a canvas's size clears it, so redraw after. */
|
|
30
|
+
resize(width, height) {
|
|
31
|
+
super.resize(width, height);
|
|
32
|
+
this.canvas.width = width;
|
|
33
|
+
this.canvas.height = height;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
clear() {
|
|
37
|
+
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
38
|
+
this._shapes = [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Always returns an isolated Path2D — used by hitTest() to test one
|
|
42
|
+
// shape's geometry at a time. Previously rect/circle called this.ctx.rect()/
|
|
43
|
+
// .arc() directly onto the canvas's own current path without resetting it
|
|
44
|
+
// first, so each hitTest() loop iteration kept accumulating geometry from
|
|
45
|
+
// every shape checked before it, corrupting hit results for anything but
|
|
46
|
+
// the very first shape tested (tooltips/clicks/hover on Canvas charts
|
|
47
|
+
// could resolve to the wrong shape).
|
|
48
|
+
_drawPath(shape) {
|
|
49
|
+
const p = new Path2D();
|
|
50
|
+
switch (shape.type) {
|
|
51
|
+
case 'rect':
|
|
52
|
+
p.rect(shape.x, shape.y, shape.width, shape.height);
|
|
53
|
+
return p;
|
|
54
|
+
case 'circle':
|
|
55
|
+
p.arc(shape.cx, shape.cy, shape.r, 0, 2 * Math.PI);
|
|
56
|
+
return p;
|
|
57
|
+
case 'path':
|
|
58
|
+
return new Path2D(shape.d);
|
|
59
|
+
default:
|
|
60
|
+
return p; // empty — no geometry to hit-test
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
_setShapeType={
|
|
64
|
+
rect:(shape)=>this.ctx.rect(shape.x, shape.y, shape.width, shape.height),
|
|
65
|
+
circle:(shape)=>this.ctx.arc(shape.cx, shape.cy, shape.r, 0, 2 * Math.PI),
|
|
66
|
+
path:(shape,fill,stroke)=>{
|
|
67
|
+
const p = new Path2D(shape.d);
|
|
68
|
+
if (fill && fill !== 'none') { this.ctx.fillStyle = fill; this.ctx.fill(p); }
|
|
69
|
+
if (stroke && stroke.color !== 'none') {
|
|
70
|
+
this.ctx.strokeStyle = stroke.color;
|
|
71
|
+
this.ctx.lineWidth = stroke.width || 1;
|
|
72
|
+
// Used by the gauge arcs' rounded end-caps ({cap: 'round'}).
|
|
73
|
+
this.ctx.lineCap = stroke.cap || 'butt';
|
|
74
|
+
this.ctx.stroke(p);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Shared by text() (initial draw) and _draw()'s 'text' case (redraw on
|
|
79
|
+
// hover enter/leave) so rotation/alignment logic isn't duplicated.
|
|
80
|
+
_paintText(shape) {
|
|
81
|
+
const hoverStyle = shape._hoverStyle;
|
|
82
|
+
const fill = hoverStyle?.fill ?? shape.fill;
|
|
83
|
+
this.ctx.fillStyle = fill || 'black';
|
|
84
|
+
this.ctx.font = `${shape.fontStyle || ''} ${shape.fontWeight || ''} ${shape.fontSize || '12px'} ${shape.fontFamily || 'sans-serif'}`.trim().replace(/\s+/g, ' ');
|
|
85
|
+
let textAlign = shape.textAnchor || 'start';
|
|
86
|
+
if (textAlign === 'middle') textAlign = 'center';
|
|
87
|
+
this.ctx.textAlign = textAlign;
|
|
88
|
+
this.ctx.textBaseline = 'middle';
|
|
89
|
+
if (shape.rotation) {
|
|
90
|
+
this.ctx.save();
|
|
91
|
+
this.ctx.translate(shape.x, shape.y);
|
|
92
|
+
this.ctx.rotate(shape.rotation);
|
|
93
|
+
this.ctx.fillText(shape.text, 0, 0);
|
|
94
|
+
this.ctx.restore();
|
|
95
|
+
} else {
|
|
96
|
+
this.ctx.fillText(shape.text, shape.x, shape.y);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
_draw(shape) {
|
|
100
|
+
if (shape.type === 'text') { this._paintText(shape); return; }
|
|
101
|
+
|
|
102
|
+
const hoverStyle = shape._hoverStyle;
|
|
103
|
+
const fill = hoverStyle?.fill ?? shape.fill;
|
|
104
|
+
const stroke = hoverStyle?.stroke ?? shape.stroke;
|
|
105
|
+
|
|
106
|
+
// For path, we need to handle fill and stroke separately inside the case.
|
|
107
|
+
const isPath = shape.type === 'path';
|
|
108
|
+
|
|
109
|
+
this.ctx.beginPath();
|
|
110
|
+
this._setShapeType[shape.type](shape, fill, stroke);
|
|
111
|
+
if (isPath) return
|
|
112
|
+
if (fill && fill !== 'none') {
|
|
113
|
+
this.ctx.fillStyle = fill;
|
|
114
|
+
this.ctx.fill();
|
|
115
|
+
}
|
|
116
|
+
if (stroke) {
|
|
117
|
+
this.ctx.strokeStyle = stroke.color || 'none';
|
|
118
|
+
this.ctx.lineWidth = stroke.width || 1;
|
|
119
|
+
this.ctx.stroke();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_redraw() {
|
|
124
|
+
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
125
|
+
this._shapes.forEach(shape => this._draw(shape));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
rect(x, y, width, height, fill, stroke, extra = {}) {
|
|
129
|
+
const shape = { type: 'rect', x, y, width, height, fill, stroke, id: `shape_${this._shapes.length}`, ...extra };
|
|
130
|
+
this._shapes.push(shape);
|
|
131
|
+
this._draw(shape);
|
|
132
|
+
return shape;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
circle(cx, cy, r, fill, stroke, extra = {}) {
|
|
136
|
+
const shape = { type: 'circle', cx, cy, r, fill, stroke, id: `shape_${this._shapes.length}`, ...extra };
|
|
137
|
+
this._shapes.push(shape);
|
|
138
|
+
this._draw(shape);
|
|
139
|
+
return shape;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
path(d, fill, stroke, extra = {}) {
|
|
143
|
+
const shape = { type: 'path', d, fill, stroke, id: `shape_${this._shapes.length}`, ...extra };
|
|
144
|
+
this._shapes.push(shape);
|
|
145
|
+
this._draw(shape);
|
|
146
|
+
return shape;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Text width measured by this renderer's own 2D context. */
|
|
150
|
+
measureText(text, font = {}) {
|
|
151
|
+
if (!this.ctx?.measureText) return super.measureText(text, font);
|
|
152
|
+
this.ctx.save();
|
|
153
|
+
this.ctx.font = BaseRenderer.cssFont(font);
|
|
154
|
+
const width = this.ctx.measureText(String(text)).width;
|
|
155
|
+
this.ctx.restore();
|
|
156
|
+
return width;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
text(text, x, y, options = {}) {
|
|
160
|
+
const shape = { type: 'text', text, x, y, ...options, id: `shape_${this._shapes.length}` };
|
|
161
|
+
this._shapes.push(shape);
|
|
162
|
+
this._paintText(shape);
|
|
163
|
+
return shape;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
hitTest(x, y) {
|
|
167
|
+
for (let i = this._shapes.length - 1; i >= 0; i--) {
|
|
168
|
+
const shape = this._shapes[i];
|
|
169
|
+
const path2d = this._drawPath(shape);
|
|
170
|
+
|
|
171
|
+
// 1. Check for a fill hit first.
|
|
172
|
+
if (shape.fill && shape.fill !== 'none') {
|
|
173
|
+
if (path2d ? this.ctx.isPointInPath(path2d, x, y) : this.ctx.isPointInPath(x, y)) {
|
|
174
|
+
return shape;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 2. If no fill hit, check for a stroke hit.
|
|
179
|
+
if (shape.stroke && shape.stroke.color !== 'none') {
|
|
180
|
+
this.ctx.lineWidth = shape.stroke.width || 1;
|
|
181
|
+
if (path2d ? this.ctx.isPointInStroke(path2d, x, y) : this.ctx.isPointInStroke(x, y)) {
|
|
182
|
+
return shape;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
enterState(shape, stateName) {
|
|
190
|
+
const targetShape = this._shapes.find(s => s.id === shape.id);
|
|
191
|
+
if (targetShape) {
|
|
192
|
+
targetShape._hoverStyle = { fill: 'orange', stroke: { color: '#333', width: 2 } };
|
|
193
|
+
this._redraw();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
leaveState(shape, stateName) {
|
|
198
|
+
const targetShape = this._shapes.find(s => s.id === shape.id);
|
|
199
|
+
if (targetShape) {
|
|
200
|
+
delete targetShape._hoverStyle;
|
|
201
|
+
this._redraw();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
renderDiagram(diagram) {
|
|
206
|
+
// Draw the diagram directly onto the canvas context.
|
|
207
|
+
diagram.drawOn(this.ctx);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create a linear gradient on this canvas context.
|
|
212
|
+
* @param {string} _id
|
|
213
|
+
* @param {Array<{offset: string|number, color: string}>} stops
|
|
214
|
+
* @param {{x0?: number, y0?: number, x1?: number, y1?: number}} [coords]
|
|
215
|
+
* @returns {CanvasGradient|null}
|
|
216
|
+
*/
|
|
217
|
+
linearGradient(_id, stops = [], { x0 = 0, y0 = 0, x1 = 0, y1 = 100 } = {}) {
|
|
218
|
+
if (!this.ctx?.createLinearGradient) return null;
|
|
219
|
+
const grad = this.ctx.createLinearGradient(x0, y0, x1, y1);
|
|
220
|
+
for (const stop of stops) {
|
|
221
|
+
const offset = typeof stop.offset === 'number' ? stop.offset : (parseFloat(stop.offset) || 0) / 100;
|
|
222
|
+
grad.addColorStop(offset, stop.color);
|
|
223
|
+
}
|
|
224
|
+
return grad;
|
|
225
|
+
}
|
|
201
226
|
}
|