@qfo/qfchart 0.7.3 → 0.8.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.
Files changed (59) hide show
  1. package/dist/index.d.ts +368 -14
  2. package/dist/qfchart.min.browser.js +34 -16
  3. package/dist/qfchart.min.es.js +34 -16
  4. package/package.json +1 -1
  5. package/src/QFChart.ts +460 -311
  6. package/src/components/AbstractPlugin.ts +234 -104
  7. package/src/components/DrawingEditor.ts +297 -248
  8. package/src/components/DrawingRendererRegistry.ts +13 -0
  9. package/src/components/GraphicBuilder.ts +284 -263
  10. package/src/components/LayoutManager.ts +72 -55
  11. package/src/components/SeriesBuilder.ts +110 -6
  12. package/src/components/TableCanvasRenderer.ts +467 -0
  13. package/src/components/TableOverlayRenderer.ts +38 -9
  14. package/src/components/TooltipFormatter.ts +97 -97
  15. package/src/components/renderers/BackgroundRenderer.ts +59 -47
  16. package/src/components/renderers/BoxRenderer.ts +113 -17
  17. package/src/components/renderers/FillRenderer.ts +118 -3
  18. package/src/components/renderers/LabelRenderer.ts +35 -9
  19. package/src/components/renderers/OHLCBarRenderer.ts +171 -161
  20. package/src/components/renderers/PolylineRenderer.ts +26 -19
  21. package/src/index.ts +17 -6
  22. package/src/plugins/ABCDPatternTool/ABCDPatternDrawingRenderer.ts +112 -0
  23. package/src/plugins/ABCDPatternTool/ABCDPatternTool.ts +136 -0
  24. package/src/plugins/ABCDPatternTool/index.ts +2 -0
  25. package/src/plugins/CypherPatternTool/CypherPatternDrawingRenderer.ts +80 -0
  26. package/src/plugins/CypherPatternTool/CypherPatternTool.ts +84 -0
  27. package/src/plugins/CypherPatternTool/index.ts +2 -0
  28. package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanDrawingRenderer.ts +163 -0
  29. package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanTool.ts +210 -0
  30. package/src/plugins/FibSpeedResistanceFanTool/index.ts +2 -0
  31. package/src/plugins/FibTrendExtensionTool/FibTrendExtensionDrawingRenderer.ts +141 -0
  32. package/src/plugins/FibTrendExtensionTool/FibTrendExtensionTool.ts +188 -0
  33. package/src/plugins/FibTrendExtensionTool/index.ts +2 -0
  34. package/src/plugins/FibonacciChannelTool/FibonacciChannelDrawingRenderer.ts +128 -0
  35. package/src/plugins/FibonacciChannelTool/FibonacciChannelTool.ts +231 -0
  36. package/src/plugins/FibonacciChannelTool/index.ts +2 -0
  37. package/src/plugins/FibonacciTool/FibonacciDrawingRenderer.ts +107 -0
  38. package/src/plugins/{FibonacciTool.ts → FibonacciTool/FibonacciTool.ts} +195 -192
  39. package/src/plugins/FibonacciTool/index.ts +2 -0
  40. package/src/plugins/HeadAndShouldersTool/HeadAndShouldersDrawingRenderer.ts +95 -0
  41. package/src/plugins/HeadAndShouldersTool/HeadAndShouldersTool.ts +97 -0
  42. package/src/plugins/HeadAndShouldersTool/index.ts +2 -0
  43. package/src/plugins/LineTool/LineDrawingRenderer.ts +49 -0
  44. package/src/plugins/{LineTool.ts → LineTool/LineTool.ts} +161 -190
  45. package/src/plugins/LineTool/index.ts +2 -0
  46. package/src/plugins/{MeasureTool.ts → MeasureTool/MeasureTool.ts} +324 -344
  47. package/src/plugins/MeasureTool/index.ts +1 -0
  48. package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternDrawingRenderer.ts +106 -0
  49. package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternTool.ts +98 -0
  50. package/src/plugins/ThreeDrivesPatternTool/index.ts +2 -0
  51. package/src/plugins/ToolGroup.ts +211 -0
  52. package/src/plugins/TrianglePatternTool/TrianglePatternDrawingRenderer.ts +107 -0
  53. package/src/plugins/TrianglePatternTool/TrianglePatternTool.ts +98 -0
  54. package/src/plugins/TrianglePatternTool/index.ts +2 -0
  55. package/src/plugins/XABCDPatternTool/XABCDPatternDrawingRenderer.ts +178 -0
  56. package/src/plugins/XABCDPatternTool/XABCDPatternTool.ts +213 -0
  57. package/src/plugins/XABCDPatternTool/index.ts +2 -0
  58. package/src/types.ts +39 -4
  59. package/src/utils/ColorUtils.ts +1 -1
@@ -0,0 +1,97 @@
1
+ import * as echarts from 'echarts';
2
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
3
+ import { HeadAndShouldersDrawingRenderer } from './HeadAndShouldersDrawingRenderer';
4
+
5
+ const LABELS = ['', 'LS', '', 'H', '', 'RS', ''];
6
+ const TOTAL_POINTS = 7;
7
+
8
+ export class HeadAndShouldersTool extends AbstractPlugin {
9
+ private points: number[][] = [];
10
+ private state: 'idle' | 'drawing' | 'finished' = 'idle';
11
+ private graphicGroup: any = null;
12
+
13
+ constructor(options: { name?: string; icon?: string } = {}) {
14
+ super({
15
+ id: 'head-and-shoulders-tool',
16
+ name: options.name || 'Head & Shoulders',
17
+ icon: options.icon || `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#e3e3e3" stroke-width="1.5"><polyline points="1,18 4,10 7,14 12,3 17,14 20,10 23,18"/></svg>`,
18
+ });
19
+ }
20
+
21
+ protected onInit(): void { this.context.registerDrawingRenderer(new HeadAndShouldersDrawingRenderer()); }
22
+
23
+ protected onActivate(): void {
24
+ this.state = 'idle'; this.points = [];
25
+ this.context.getChart().getZr().setCursorStyle('crosshair');
26
+ const zr = this.context.getChart().getZr();
27
+ zr.on('click', this.onClick); zr.on('mousemove', this.onMouseMove);
28
+ }
29
+
30
+ protected onDeactivate(): void {
31
+ this.state = 'idle'; this.points = []; this.removeGraphic();
32
+ const zr = this.context.getChart().getZr();
33
+ zr.off('click', this.onClick); zr.off('mousemove', this.onMouseMove);
34
+ zr.setCursorStyle('default');
35
+ }
36
+
37
+ private onClick = (params: any) => {
38
+ const pt = this.getPoint(params);
39
+ if (this.state === 'idle') {
40
+ this.state = 'drawing'; this.points = [pt, [...pt]]; this.initGraphic(); this.updateGraphic();
41
+ } else if (this.state === 'drawing') {
42
+ this.points[this.points.length - 1] = pt;
43
+ if (this.points.length >= TOTAL_POINTS) {
44
+ this.state = 'finished'; this.updateGraphic(); this.saveDrawing(); this.removeGraphic(); this.context.disableTools();
45
+ } else { this.points.push([...pt]); this.updateGraphic(); }
46
+ }
47
+ };
48
+
49
+ private onMouseMove = (params: any) => {
50
+ if (this.state !== 'drawing' || this.points.length < 2) return;
51
+ this.points[this.points.length - 1] = this.getPoint(params); this.updateGraphic();
52
+ };
53
+
54
+ private initGraphic() { this.graphicGroup = new echarts.graphic.Group(); this.context.getChart().getZr().add(this.graphicGroup); }
55
+ private removeGraphic() { if (this.graphicGroup) { this.context.getChart().getZr().remove(this.graphicGroup); this.graphicGroup = null; } }
56
+
57
+ private updateGraphic() {
58
+ if (!this.graphicGroup) return;
59
+ this.graphicGroup.removeAll();
60
+ const pts = this.points;
61
+
62
+ // Fills
63
+ if (pts.length >= 3) this.graphicGroup.add(new echarts.graphic.Polygon({ shape: { points: pts.slice(0, 3) }, style: { fill: 'rgba(33,150,243,0.06)' }, silent: true }));
64
+ if (pts.length >= 5) this.graphicGroup.add(new echarts.graphic.Polygon({ shape: { points: pts.slice(2, 5) }, style: { fill: 'rgba(244,67,54,0.08)' }, silent: true }));
65
+ if (pts.length >= 7) this.graphicGroup.add(new echarts.graphic.Polygon({ shape: { points: pts.slice(4, 7) }, style: { fill: 'rgba(33,150,243,0.06)' }, silent: true }));
66
+
67
+ // Zigzag
68
+ for (let i = 0; i < pts.length - 1; i++) {
69
+ this.graphicGroup.add(new echarts.graphic.Line({ shape: { x1: pts[i][0], y1: pts[i][1], x2: pts[i + 1][0], y2: pts[i + 1][1] }, style: { stroke: '#2196f3', lineWidth: 2 }, silent: true }));
70
+ }
71
+
72
+ // Neckline
73
+ if (pts.length >= 5) {
74
+ const [nx1, ny1] = pts[2];
75
+ const [nx2, ny2] = pts[4];
76
+ const dx = nx2 - nx1; const dy = ny2 - ny1;
77
+ this.graphicGroup.add(new echarts.graphic.Line({ shape: { x1: nx1 - dx * 0.3, y1: ny1 - dy * 0.3, x2: nx2 + dx * 0.3, y2: ny2 + dy * 0.3 }, style: { stroke: '#ff9800', lineWidth: 2, lineDash: [6, 4] }, silent: true }));
78
+ }
79
+
80
+ // Labels & circles
81
+ for (let i = 0; i < pts.length && i < LABELS.length; i++) {
82
+ const [px, py] = pts[i];
83
+ const isHigh = (i === 0 || py <= pts[i - 1][1]) && (i === pts.length - 1 || py <= pts[i + 1]?.[1]);
84
+ if (LABELS[i]) {
85
+ this.graphicGroup.add(new echarts.graphic.Text({ style: { text: LABELS[i], x: px, y: isHigh ? py - 14 : py + 16, fill: '#e2e8f0', fontSize: 12, fontWeight: 'bold', align: 'center', verticalAlign: 'middle' }, silent: true }));
86
+ }
87
+ this.graphicGroup.add(new echarts.graphic.Circle({ shape: { cx: px, cy: py, r: 4 }, style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 1.5 }, z: 101, silent: true }));
88
+ }
89
+ }
90
+
91
+ private saveDrawing() {
92
+ const dataPoints = this.points.map((pt) => this.context.coordinateConversion.pixelToData({ x: pt[0], y: pt[1] }));
93
+ if (dataPoints.every((p) => p !== null)) {
94
+ this.context.addDrawing({ id: `hs-${Date.now()}`, type: 'head_and_shoulders', points: dataPoints as any[], paneIndex: dataPoints[0]!.paneIndex || 0, style: { color: '#3b82f6', lineWidth: 2 } });
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,2 @@
1
+ export { HeadAndShouldersTool } from './HeadAndShouldersTool';
2
+ export { HeadAndShouldersDrawingRenderer } from './HeadAndShouldersDrawingRenderer';
@@ -0,0 +1,49 @@
1
+ import { DrawingRenderer, DrawingRenderContext } from '../../types';
2
+
3
+ export class LineDrawingRenderer implements DrawingRenderer {
4
+ type = 'line';
5
+
6
+ render(ctx: DrawingRenderContext): any {
7
+ const { drawing, pixelPoints, isSelected } = ctx;
8
+ const [x1, y1] = pixelPoints[0];
9
+ const [x2, y2] = pixelPoints[1];
10
+ const color = drawing.style?.color || '#3b82f6';
11
+
12
+ return {
13
+ type: 'group',
14
+ children: [
15
+ {
16
+ type: 'line',
17
+ name: 'line',
18
+ shape: { x1, y1, x2, y2 },
19
+ style: {
20
+ stroke: color,
21
+ lineWidth: drawing.style?.lineWidth || 2,
22
+ },
23
+ },
24
+ {
25
+ type: 'circle',
26
+ name: 'point-0',
27
+ shape: { cx: x1, cy: y1, r: 4 },
28
+ style: {
29
+ fill: '#fff',
30
+ stroke: color,
31
+ lineWidth: 1,
32
+ opacity: isSelected ? 1 : 0,
33
+ },
34
+ },
35
+ {
36
+ type: 'circle',
37
+ name: 'point-1',
38
+ shape: { cx: x2, cy: y2, r: 4 },
39
+ style: {
40
+ fill: '#fff',
41
+ stroke: color,
42
+ lineWidth: 1,
43
+ opacity: isSelected ? 1 : 0,
44
+ },
45
+ },
46
+ ],
47
+ };
48
+ }
49
+ }
@@ -1,190 +1,161 @@
1
- import { AbstractPlugin } from '../components/AbstractPlugin';
2
- import * as echarts from 'echarts';
3
-
4
- type PluginState = 'idle' | 'drawing' | 'finished';
5
-
6
- export class LineTool extends AbstractPlugin {
7
- private zr!: any;
8
- private state: PluginState = 'idle';
9
- private startPoint: number[] | null = null;
10
- private endPoint: number[] | null = null;
11
-
12
- // ZRender Elements
13
- private group: any = null;
14
- private line: any = null;
15
- private startCircle: any = null;
16
- private endCircle: any = null;
17
-
18
- constructor(options: { name?: string; icon?: string }) {
19
- super({
20
- id: 'trend-line',
21
- name: options?.name || 'Trend Line',
22
- icon:
23
- options?.icon ||
24
- `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="2" y1="22" x2="22" y2="2" /></svg>`,
25
- });
26
- }
27
-
28
- protected onInit(): void {
29
- this.zr = this.chart.getZr();
30
- }
31
-
32
- protected onActivate(): void {
33
- this.state = 'idle';
34
- this.chart.getZr().setCursorStyle('crosshair');
35
-
36
- this.zr.on('click', this.onClick);
37
- this.zr.on('mousemove', this.onMouseMove);
38
- }
39
-
40
- protected onDeactivate(): void {
41
- this.state = 'idle';
42
- this.chart.getZr().setCursorStyle('default');
43
-
44
- this.zr.off('click', this.onClick);
45
- this.zr.off('mousemove', this.onMouseMove);
46
-
47
- // Clean up clear listeners
48
- this.disableClearListeners();
49
-
50
- // @ts-ignore - state type comparison
51
- if (this.state === 'drawing') {
52
- this.removeGraphic();
53
- }
54
- }
55
-
56
- protected onDestroy(): void {
57
- this.removeGraphic();
58
- }
59
-
60
- // --- Interaction Handlers ---
61
-
62
- private onMouseDown = () => {
63
- // No longer needed
64
- };
65
-
66
- private onChartInteraction = () => {
67
- // No longer needed
68
- };
69
-
70
- private onClick = (params: any) => {
71
- if (this.state === 'idle') {
72
- this.state = 'drawing';
73
- this.startPoint = [params.offsetX, params.offsetY];
74
- this.endPoint = [params.offsetX, params.offsetY];
75
- this.initGraphic();
76
- this.updateGraphic();
77
- } else if (this.state === 'drawing') {
78
- this.state = 'finished';
79
- this.endPoint = [params.offsetX, params.offsetY];
80
- this.updateGraphic();
81
-
82
- // Convert to native chart drawing
83
- if (this.startPoint && this.endPoint) {
84
- const start = this.context.coordinateConversion.pixelToData({
85
- x: this.startPoint[0],
86
- y: this.startPoint[1],
87
- });
88
- const end = this.context.coordinateConversion.pixelToData({
89
- x: this.endPoint[0],
90
- y: this.endPoint[1],
91
- });
92
-
93
- if (start && end) {
94
- // Use the pane index from the start point (assume drawing starts and ends in same pane or use start pane)
95
- const paneIndex = start.paneIndex || 0;
96
-
97
- this.context.addDrawing({
98
- id: `line-${Date.now()}`,
99
- type: 'line',
100
- points: [start, end],
101
- paneIndex: paneIndex,
102
- style: {
103
- color: '#3b82f6',
104
- lineWidth: 2,
105
- },
106
- });
107
- }
108
- }
109
-
110
- // Cleanup local ZRender graphic as it's now part of the chart series
111
- this.removeGraphic();
112
- this.context.disableTools();
113
- }
114
- };
115
-
116
- private saveDataCoordinates() {
117
- // No longer needed
118
- }
119
-
120
- private updateGraphicFromData() {
121
- // No longer needed
122
- }
123
-
124
- private enableClearListeners(): void {
125
- // No longer needed
126
- }
127
-
128
- private clearHandlers: any = {};
129
-
130
- private disableClearListeners(): void {
131
- // No longer needed
132
- }
133
-
134
- private onMouseMove = (params: any) => {
135
- if (this.state !== 'drawing') return;
136
- this.endPoint = [params.offsetX, params.offsetY];
137
- this.updateGraphic();
138
- };
139
-
140
- // --- Graphics ---
141
-
142
- private initGraphic(): void {
143
- if (this.group) return;
144
-
145
- this.group = new echarts.graphic.Group();
146
-
147
- this.line = new echarts.graphic.Line({
148
- shape: { x1: 0, y1: 0, x2: 0, y2: 0 },
149
- style: { stroke: '#3b82f6', lineWidth: 2 },
150
- z: 100,
151
- });
152
-
153
- this.startCircle = new echarts.graphic.Circle({
154
- shape: { cx: 0, cy: 0, r: 4 },
155
- style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 1 },
156
- z: 101,
157
- });
158
-
159
- this.endCircle = new echarts.graphic.Circle({
160
- shape: { cx: 0, cy: 0, r: 4 },
161
- style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 1 },
162
- z: 101,
163
- });
164
-
165
- this.group.add(this.line);
166
- this.group.add(this.startCircle);
167
- this.group.add(this.endCircle);
168
-
169
- this.zr.add(this.group);
170
- }
171
-
172
- private removeGraphic(): void {
173
- if (this.group) {
174
- this.zr.remove(this.group);
175
- this.group = null;
176
- this.disableClearListeners();
177
- }
178
- }
179
-
180
- private updateGraphic(): void {
181
- if (!this.startPoint || !this.endPoint || !this.group) return;
182
-
183
- const [x1, y1] = this.startPoint;
184
- const [x2, y2] = this.endPoint;
185
-
186
- this.line.setShape({ x1, y1, x2, y2 });
187
- this.startCircle.setShape({ cx: x1, cy: y1 });
188
- this.endCircle.setShape({ cx: x2, cy: y2 });
189
- }
190
- }
1
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
2
+ import { LineDrawingRenderer } from './LineDrawingRenderer';
3
+ import * as echarts from 'echarts';
4
+
5
+ type PluginState = 'idle' | 'drawing' | 'finished';
6
+
7
+ export class LineTool extends AbstractPlugin {
8
+ private zr!: any;
9
+ private state: PluginState = 'idle';
10
+ private startPoint: number[] | null = null;
11
+ private endPoint: number[] | null = null;
12
+
13
+ // ZRender Elements
14
+ private group: any = null;
15
+ private line: any = null;
16
+ private startCircle: any = null;
17
+ private endCircle: any = null;
18
+
19
+ constructor(options: { name?: string; icon?: string } = {}) {
20
+ super({
21
+ id: 'trend-line',
22
+ name: options?.name || 'Trend Line',
23
+ icon:
24
+ options?.icon ||
25
+ `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="2" y1="22" x2="22" y2="2" /></svg>`,
26
+ });
27
+ }
28
+
29
+ protected onInit(): void {
30
+ this.zr = this.chart.getZr();
31
+ this.context.registerDrawingRenderer(new LineDrawingRenderer());
32
+ }
33
+
34
+ protected onActivate(): void {
35
+ this.state = 'idle';
36
+ this.chart.getZr().setCursorStyle('crosshair');
37
+
38
+ this.zr.on('click', this.onClick);
39
+ this.zr.on('mousemove', this.onMouseMove);
40
+ }
41
+
42
+ protected onDeactivate(): void {
43
+ this.state = 'idle';
44
+ this.chart.getZr().setCursorStyle('default');
45
+
46
+ this.zr.off('click', this.onClick);
47
+ this.zr.off('mousemove', this.onMouseMove);
48
+
49
+ // @ts-ignore - state type comparison
50
+ if (this.state === 'drawing') {
51
+ this.removeGraphic();
52
+ }
53
+ }
54
+
55
+ protected onDestroy(): void {
56
+ this.removeGraphic();
57
+ }
58
+
59
+ // --- Interaction Handlers ---
60
+
61
+ private onClick = (params: any) => {
62
+ if (this.state === 'idle') {
63
+ this.state = 'drawing';
64
+ this.startPoint = this.getPoint(params);
65
+ this.endPoint = this.getPoint(params);
66
+ this.initGraphic();
67
+ this.updateGraphic();
68
+ } else if (this.state === 'drawing') {
69
+ this.state = 'finished';
70
+ this.endPoint = this.getPoint(params);
71
+ this.updateGraphic();
72
+
73
+ // Convert to native chart drawing
74
+ if (this.startPoint && this.endPoint) {
75
+ const start = this.context.coordinateConversion.pixelToData({
76
+ x: this.startPoint[0],
77
+ y: this.startPoint[1],
78
+ });
79
+ const end = this.context.coordinateConversion.pixelToData({
80
+ x: this.endPoint[0],
81
+ y: this.endPoint[1],
82
+ });
83
+
84
+ if (start && end) {
85
+ const paneIndex = start.paneIndex || 0;
86
+
87
+ this.context.addDrawing({
88
+ id: `line-${Date.now()}`,
89
+ type: 'line',
90
+ points: [start, end],
91
+ paneIndex: paneIndex,
92
+ style: {
93
+ color: '#3b82f6',
94
+ lineWidth: 2,
95
+ },
96
+ });
97
+ }
98
+ }
99
+
100
+ // Cleanup local ZRender graphic as it's now part of the chart series
101
+ this.removeGraphic();
102
+ this.context.disableTools();
103
+ }
104
+ };
105
+
106
+ private onMouseMove = (params: any) => {
107
+ if (this.state !== 'drawing') return;
108
+ this.endPoint = this.getPoint(params);
109
+ this.updateGraphic();
110
+ };
111
+
112
+ // --- Graphics ---
113
+
114
+ private initGraphic(): void {
115
+ if (this.group) return;
116
+
117
+ this.group = new echarts.graphic.Group();
118
+
119
+ this.line = new echarts.graphic.Line({
120
+ shape: { x1: 0, y1: 0, x2: 0, y2: 0 },
121
+ style: { stroke: '#3b82f6', lineWidth: 2 },
122
+ z: 100,
123
+ });
124
+
125
+ this.startCircle = new echarts.graphic.Circle({
126
+ shape: { cx: 0, cy: 0, r: 4 },
127
+ style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 1 },
128
+ z: 101,
129
+ });
130
+
131
+ this.endCircle = new echarts.graphic.Circle({
132
+ shape: { cx: 0, cy: 0, r: 4 },
133
+ style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 1 },
134
+ z: 101,
135
+ });
136
+
137
+ this.group.add(this.line);
138
+ this.group.add(this.startCircle);
139
+ this.group.add(this.endCircle);
140
+
141
+ this.zr.add(this.group);
142
+ }
143
+
144
+ private removeGraphic(): void {
145
+ if (this.group) {
146
+ this.zr.remove(this.group);
147
+ this.group = null;
148
+ }
149
+ }
150
+
151
+ private updateGraphic(): void {
152
+ if (!this.startPoint || !this.endPoint || !this.group) return;
153
+
154
+ const [x1, y1] = this.startPoint;
155
+ const [x2, y2] = this.endPoint;
156
+
157
+ this.line.setShape({ x1, y1, x2, y2 });
158
+ this.startCircle.setShape({ cx: x1, cy: y1 });
159
+ this.endCircle.setShape({ cx: x2, cy: y2 });
160
+ }
161
+ }
@@ -0,0 +1,2 @@
1
+ export { LineTool } from './LineTool';
2
+ export { LineDrawingRenderer } from './LineDrawingRenderer';