@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.
- package/dist/index.d.ts +368 -14
- package/dist/qfchart.min.browser.js +34 -16
- package/dist/qfchart.min.es.js +34 -16
- package/package.json +1 -1
- package/src/QFChart.ts +460 -311
- package/src/components/AbstractPlugin.ts +234 -104
- package/src/components/DrawingEditor.ts +297 -248
- package/src/components/DrawingRendererRegistry.ts +13 -0
- package/src/components/GraphicBuilder.ts +284 -263
- package/src/components/LayoutManager.ts +72 -55
- package/src/components/SeriesBuilder.ts +110 -6
- package/src/components/TableCanvasRenderer.ts +467 -0
- package/src/components/TableOverlayRenderer.ts +38 -9
- package/src/components/TooltipFormatter.ts +97 -97
- package/src/components/renderers/BackgroundRenderer.ts +59 -47
- package/src/components/renderers/BoxRenderer.ts +113 -17
- package/src/components/renderers/FillRenderer.ts +118 -3
- package/src/components/renderers/LabelRenderer.ts +35 -9
- package/src/components/renderers/OHLCBarRenderer.ts +171 -161
- package/src/components/renderers/PolylineRenderer.ts +26 -19
- package/src/index.ts +17 -6
- package/src/plugins/ABCDPatternTool/ABCDPatternDrawingRenderer.ts +112 -0
- package/src/plugins/ABCDPatternTool/ABCDPatternTool.ts +136 -0
- package/src/plugins/ABCDPatternTool/index.ts +2 -0
- package/src/plugins/CypherPatternTool/CypherPatternDrawingRenderer.ts +80 -0
- package/src/plugins/CypherPatternTool/CypherPatternTool.ts +84 -0
- package/src/plugins/CypherPatternTool/index.ts +2 -0
- package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanDrawingRenderer.ts +163 -0
- package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanTool.ts +210 -0
- package/src/plugins/FibSpeedResistanceFanTool/index.ts +2 -0
- package/src/plugins/FibTrendExtensionTool/FibTrendExtensionDrawingRenderer.ts +141 -0
- package/src/plugins/FibTrendExtensionTool/FibTrendExtensionTool.ts +188 -0
- package/src/plugins/FibTrendExtensionTool/index.ts +2 -0
- package/src/plugins/FibonacciChannelTool/FibonacciChannelDrawingRenderer.ts +128 -0
- package/src/plugins/FibonacciChannelTool/FibonacciChannelTool.ts +231 -0
- package/src/plugins/FibonacciChannelTool/index.ts +2 -0
- package/src/plugins/FibonacciTool/FibonacciDrawingRenderer.ts +107 -0
- package/src/plugins/{FibonacciTool.ts → FibonacciTool/FibonacciTool.ts} +195 -192
- package/src/plugins/FibonacciTool/index.ts +2 -0
- package/src/plugins/HeadAndShouldersTool/HeadAndShouldersDrawingRenderer.ts +95 -0
- package/src/plugins/HeadAndShouldersTool/HeadAndShouldersTool.ts +97 -0
- package/src/plugins/HeadAndShouldersTool/index.ts +2 -0
- package/src/plugins/LineTool/LineDrawingRenderer.ts +49 -0
- package/src/plugins/{LineTool.ts → LineTool/LineTool.ts} +161 -190
- package/src/plugins/LineTool/index.ts +2 -0
- package/src/plugins/{MeasureTool.ts → MeasureTool/MeasureTool.ts} +324 -344
- package/src/plugins/MeasureTool/index.ts +1 -0
- package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternDrawingRenderer.ts +106 -0
- package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternTool.ts +98 -0
- package/src/plugins/ThreeDrivesPatternTool/index.ts +2 -0
- package/src/plugins/ToolGroup.ts +211 -0
- package/src/plugins/TrianglePatternTool/TrianglePatternDrawingRenderer.ts +107 -0
- package/src/plugins/TrianglePatternTool/TrianglePatternTool.ts +98 -0
- package/src/plugins/TrianglePatternTool/index.ts +2 -0
- package/src/plugins/XABCDPatternTool/XABCDPatternDrawingRenderer.ts +178 -0
- package/src/plugins/XABCDPatternTool/XABCDPatternTool.ts +213 -0
- package/src/plugins/XABCDPatternTool/index.ts +2 -0
- package/src/types.ts +39 -4
- package/src/utils/ColorUtils.ts +1 -1
|
@@ -24,14 +24,19 @@ export class LabelRenderer implements SeriesRenderer {
|
|
|
24
24
|
|
|
25
25
|
const labelData = labelObjects
|
|
26
26
|
.map((lbl) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
27
|
+
// Resolve any function/Series values that may not have been
|
|
28
|
+
// resolved at PineTS level (e.g. setters that skip _resolve()).
|
|
29
|
+
const resolve = (v: any) => typeof v === 'function' ? v() : v;
|
|
30
|
+
|
|
31
|
+
const text = resolve(lbl.text) || '';
|
|
32
|
+
const rawColor = resolve(lbl.color);
|
|
33
|
+
const color = (rawColor != null && rawColor !== '') ? rawColor : 'transparent';
|
|
34
|
+
const textcolor = resolve(lbl.textcolor) || '#ffffff';
|
|
35
|
+
const yloc = resolve(lbl.yloc) || 'price';
|
|
36
|
+
const styleRaw = resolve(lbl.style) || 'style_label_down';
|
|
37
|
+
const size = resolve(lbl.size) || 'normal';
|
|
38
|
+
const textalign = resolve(lbl.textalign) || 'align_center';
|
|
39
|
+
const tooltip = resolve(lbl.tooltip) || '';
|
|
35
40
|
|
|
36
41
|
// Map Pine style string to shape name for ShapeUtils
|
|
37
42
|
const shape = this.styleToShape(styleRaw);
|
|
@@ -160,7 +165,24 @@ export class LabelRenderer implements SeriesRenderer {
|
|
|
160
165
|
};
|
|
161
166
|
|
|
162
167
|
if (tooltip) {
|
|
163
|
-
|
|
168
|
+
// Store tooltip text for the custom tooltip overlay in QFChart.ts.
|
|
169
|
+
// ECharts mouseover event can read this from params.data._tooltipText.
|
|
170
|
+
item._tooltipText = tooltip;
|
|
171
|
+
// Enable emphasis for this item so ECharts fires mouseover/mouseout
|
|
172
|
+
// events, but prevent any visual change by mirroring normal styles.
|
|
173
|
+
item.emphasis = {
|
|
174
|
+
scale: false,
|
|
175
|
+
itemStyle: { color: color },
|
|
176
|
+
label: {
|
|
177
|
+
show: item.label.show,
|
|
178
|
+
color: textcolor,
|
|
179
|
+
fontSize: fontSize,
|
|
180
|
+
fontWeight: 'bold',
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
} else {
|
|
184
|
+
// No tooltip: fully disable emphasis (no hover interaction)
|
|
185
|
+
item.emphasis = { disabled: true };
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
return item;
|
|
@@ -174,6 +196,10 @@ export class LabelRenderer implements SeriesRenderer {
|
|
|
174
196
|
yAxisIndex: yAxisIndex,
|
|
175
197
|
data: labelData,
|
|
176
198
|
z: 20,
|
|
199
|
+
// Per-item emphasis: disabled for labels without tooltips,
|
|
200
|
+
// scale:false for labels with tooltips (allows hover for custom tooltip).
|
|
201
|
+
animation: false, // Prevent labels disappearing on zoom
|
|
202
|
+
clip: false, // Keep labels visible when partially outside viewport
|
|
177
203
|
};
|
|
178
204
|
}
|
|
179
205
|
|
|
@@ -1,161 +1,171 @@
|
|
|
1
|
-
import { SeriesRenderer, RenderContext } from './SeriesRenderer';
|
|
2
|
-
|
|
3
|
-
export class OHLCBarRenderer implements SeriesRenderer {
|
|
4
|
-
render(context: RenderContext): any {
|
|
5
|
-
const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, optionsArray, plotOptions } = context;
|
|
6
|
-
const defaultColor = '#2962ff';
|
|
7
|
-
const isCandle = plotOptions.style === 'candle';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
1
|
+
import { SeriesRenderer, RenderContext } from './SeriesRenderer';
|
|
2
|
+
|
|
3
|
+
export class OHLCBarRenderer implements SeriesRenderer {
|
|
4
|
+
render(context: RenderContext): any {
|
|
5
|
+
const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, optionsArray, plotOptions } = context;
|
|
6
|
+
const defaultColor = '#2962ff';
|
|
7
|
+
const isCandle = plotOptions.style === 'candle';
|
|
8
|
+
|
|
9
|
+
// Build a separate color lookup — ECharts custom series coerces data values to numbers,
|
|
10
|
+
// so string colors stored in the data array would become NaN via api.value().
|
|
11
|
+
const colorLookup: { color: string; wickColor: string; borderColor: string }[] = [];
|
|
12
|
+
|
|
13
|
+
const ohlcData = dataArray
|
|
14
|
+
.map((val, i) => {
|
|
15
|
+
if (val === null || !Array.isArray(val) || val.length !== 4) return null;
|
|
16
|
+
|
|
17
|
+
const [open, high, low, close] = val;
|
|
18
|
+
const pointOpts = optionsArray[i] || {};
|
|
19
|
+
const color = pointOpts.color || colorArray[i] || plotOptions.color || defaultColor;
|
|
20
|
+
const wickColor = pointOpts.wickcolor || plotOptions.wickcolor || color;
|
|
21
|
+
const borderColor = pointOpts.bordercolor || plotOptions.bordercolor || wickColor;
|
|
22
|
+
|
|
23
|
+
// Store colors in a closure-accessible lookup keyed by the data index
|
|
24
|
+
colorLookup[i] = { color, wickColor, borderColor };
|
|
25
|
+
|
|
26
|
+
// Data array contains only numeric values for ECharts
|
|
27
|
+
return [i, open, close, low, high];
|
|
28
|
+
})
|
|
29
|
+
.filter((item) => item !== null);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
name: seriesName,
|
|
33
|
+
type: 'custom',
|
|
34
|
+
xAxisIndex: xAxisIndex,
|
|
35
|
+
yAxisIndex: yAxisIndex,
|
|
36
|
+
renderItem: (params: any, api: any) => {
|
|
37
|
+
const xValue = api.value(0);
|
|
38
|
+
const openValue = api.value(1);
|
|
39
|
+
const closeValue = api.value(2);
|
|
40
|
+
const lowValue = api.value(3);
|
|
41
|
+
const highValue = api.value(4);
|
|
42
|
+
|
|
43
|
+
if (isNaN(openValue) || isNaN(closeValue) || isNaN(lowValue) || isNaN(highValue)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Retrieve colors from the closure-based lookup using the original data index
|
|
48
|
+
const colors = colorLookup[xValue] || { color: defaultColor, wickColor: defaultColor, borderColor: defaultColor };
|
|
49
|
+
const color = colors.color;
|
|
50
|
+
const wickColor = colors.wickColor;
|
|
51
|
+
const borderColor = colors.borderColor;
|
|
52
|
+
|
|
53
|
+
const xPos = api.coord([xValue, 0])[0];
|
|
54
|
+
const openPos = api.coord([xValue, openValue])[1];
|
|
55
|
+
const closePos = api.coord([xValue, closeValue])[1];
|
|
56
|
+
const lowPos = api.coord([xValue, lowValue])[1];
|
|
57
|
+
const highPos = api.coord([xValue, highValue])[1];
|
|
58
|
+
|
|
59
|
+
const barWidth = api.size([1, 0])[0] * 0.6;
|
|
60
|
+
|
|
61
|
+
if (isCandle) {
|
|
62
|
+
// Classic candlestick rendering
|
|
63
|
+
const bodyTop = Math.min(openPos, closePos);
|
|
64
|
+
const bodyBottom = Math.max(openPos, closePos);
|
|
65
|
+
const bodyHeight = Math.abs(closePos - openPos);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
type: 'group',
|
|
69
|
+
children: [
|
|
70
|
+
// Upper wick
|
|
71
|
+
{
|
|
72
|
+
type: 'line',
|
|
73
|
+
shape: {
|
|
74
|
+
x1: xPos,
|
|
75
|
+
y1: highPos,
|
|
76
|
+
x2: xPos,
|
|
77
|
+
y2: bodyTop,
|
|
78
|
+
},
|
|
79
|
+
style: {
|
|
80
|
+
stroke: wickColor,
|
|
81
|
+
lineWidth: 1,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
// Lower wick
|
|
85
|
+
{
|
|
86
|
+
type: 'line',
|
|
87
|
+
shape: {
|
|
88
|
+
x1: xPos,
|
|
89
|
+
y1: bodyBottom,
|
|
90
|
+
x2: xPos,
|
|
91
|
+
y2: lowPos,
|
|
92
|
+
},
|
|
93
|
+
style: {
|
|
94
|
+
stroke: wickColor,
|
|
95
|
+
lineWidth: 1,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
// Body
|
|
99
|
+
{
|
|
100
|
+
type: 'rect',
|
|
101
|
+
shape: {
|
|
102
|
+
x: xPos - barWidth / 2,
|
|
103
|
+
y: bodyTop,
|
|
104
|
+
width: barWidth,
|
|
105
|
+
height: bodyHeight || 1, // Minimum height for doji
|
|
106
|
+
},
|
|
107
|
+
style: {
|
|
108
|
+
fill: color,
|
|
109
|
+
stroke: borderColor,
|
|
110
|
+
lineWidth: 1,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
} else {
|
|
116
|
+
// Bar style (OHLC bar)
|
|
117
|
+
const tickWidth = barWidth * 0.5;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
type: 'group',
|
|
121
|
+
children: [
|
|
122
|
+
// Vertical line (low to high)
|
|
123
|
+
{
|
|
124
|
+
type: 'line',
|
|
125
|
+
shape: {
|
|
126
|
+
x1: xPos,
|
|
127
|
+
y1: lowPos,
|
|
128
|
+
x2: xPos,
|
|
129
|
+
y2: highPos,
|
|
130
|
+
},
|
|
131
|
+
style: {
|
|
132
|
+
stroke: color,
|
|
133
|
+
lineWidth: 1,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
// Open tick (left)
|
|
137
|
+
{
|
|
138
|
+
type: 'line',
|
|
139
|
+
shape: {
|
|
140
|
+
x1: xPos - tickWidth,
|
|
141
|
+
y1: openPos,
|
|
142
|
+
x2: xPos,
|
|
143
|
+
y2: openPos,
|
|
144
|
+
},
|
|
145
|
+
style: {
|
|
146
|
+
stroke: color,
|
|
147
|
+
lineWidth: 1,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
// Close tick (right)
|
|
151
|
+
{
|
|
152
|
+
type: 'line',
|
|
153
|
+
shape: {
|
|
154
|
+
x1: xPos,
|
|
155
|
+
y1: closePos,
|
|
156
|
+
x2: xPos + tickWidth,
|
|
157
|
+
y2: closePos,
|
|
158
|
+
},
|
|
159
|
+
style: {
|
|
160
|
+
stroke: color,
|
|
161
|
+
lineWidth: 1,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
data: ohlcData,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -69,7 +69,12 @@ export class PolylineRenderer implements SeriesRenderer {
|
|
|
69
69
|
|
|
70
70
|
if (pixelPoints.length < 2) continue;
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
// Detect na/NaN line_color (means no stroke)
|
|
73
|
+
const rawLineColor = pl.line_color;
|
|
74
|
+
const isNaLineColor = rawLineColor === null || rawLineColor === undefined ||
|
|
75
|
+
(typeof rawLineColor === 'number' && isNaN(rawLineColor)) ||
|
|
76
|
+
rawLineColor === 'na' || rawLineColor === 'NaN';
|
|
77
|
+
const lineColor = isNaLineColor ? null : (rawLineColor || '#2962ff');
|
|
73
78
|
const lineWidth = pl.line_width || 1;
|
|
74
79
|
const dashPattern = this.getDashPattern(pl.line_style);
|
|
75
80
|
|
|
@@ -95,23 +100,25 @@ export class PolylineRenderer implements SeriesRenderer {
|
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
// Stroke (line segments)
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
// Stroke (line segments) — skip entirely if line_color is na
|
|
104
|
+
if (lineColor && lineWidth > 0) {
|
|
105
|
+
if (pl.curved) {
|
|
106
|
+
const pathData = this.buildCurvedPath(pixelPoints, pl.closed);
|
|
107
|
+
children.push({
|
|
108
|
+
type: 'path',
|
|
109
|
+
shape: { pathData },
|
|
110
|
+
style: { fill: 'none', stroke: lineColor, lineWidth, lineDash: dashPattern },
|
|
111
|
+
silent: true,
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
const allPoints = pl.closed ? [...pixelPoints, pixelPoints[0]] : pixelPoints;
|
|
115
|
+
children.push({
|
|
116
|
+
type: 'polyline',
|
|
117
|
+
shape: { points: allPoints },
|
|
118
|
+
style: { fill: 'none', stroke: lineColor, lineWidth, lineDash: dashPattern },
|
|
119
|
+
silent: true,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
115
122
|
}
|
|
116
123
|
}
|
|
117
124
|
|
|
@@ -122,7 +129,7 @@ export class PolylineRenderer implements SeriesRenderer {
|
|
|
122
129
|
encode: { x: [0, 1] },
|
|
123
130
|
// Prevent ECharts visual system from overriding element colors with palette
|
|
124
131
|
itemStyle: { color: 'transparent', borderColor: 'transparent' },
|
|
125
|
-
z:
|
|
132
|
+
z: 15,
|
|
126
133
|
silent: true,
|
|
127
134
|
emphasis: { disabled: true },
|
|
128
135
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
export * from "./types";
|
|
2
|
-
export * from "./QFChart";
|
|
3
|
-
export * from "./plugins/MeasureTool";
|
|
4
|
-
export * from "./plugins/LineTool";
|
|
5
|
-
export * from "./plugins/FibonacciTool";
|
|
6
|
-
export * from "./
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./QFChart";
|
|
3
|
+
export * from "./plugins/MeasureTool";
|
|
4
|
+
export * from "./plugins/LineTool";
|
|
5
|
+
export * from "./plugins/FibonacciTool";
|
|
6
|
+
export * from "./plugins/FibonacciChannelTool";
|
|
7
|
+
export * from "./plugins/FibSpeedResistanceFanTool";
|
|
8
|
+
export * from "./plugins/FibTrendExtensionTool";
|
|
9
|
+
export * from "./plugins/XABCDPatternTool";
|
|
10
|
+
export * from "./plugins/ABCDPatternTool";
|
|
11
|
+
export * from "./plugins/CypherPatternTool";
|
|
12
|
+
export * from "./plugins/HeadAndShouldersTool";
|
|
13
|
+
export * from "./plugins/TrianglePatternTool";
|
|
14
|
+
export * from "./plugins/ThreeDrivesPatternTool";
|
|
15
|
+
export * from "./plugins/ToolGroup";
|
|
16
|
+
export * from "./components/AbstractPlugin";
|
|
17
|
+
export * from "./components/DrawingRendererRegistry";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { DrawingRenderer, DrawingRenderContext } from '../../types';
|
|
2
|
+
|
|
3
|
+
const LABELS = ['A', 'B', 'C', 'D'];
|
|
4
|
+
const LEG_COLORS = ['#2196f3', '#ff9800', '#4caf50'];
|
|
5
|
+
|
|
6
|
+
export class ABCDPatternDrawingRenderer implements DrawingRenderer {
|
|
7
|
+
type = 'abcd_pattern';
|
|
8
|
+
|
|
9
|
+
render(ctx: DrawingRenderContext): any {
|
|
10
|
+
const { drawing, pixelPoints, isSelected } = ctx;
|
|
11
|
+
const color = drawing.style?.color || '#3b82f6';
|
|
12
|
+
if (pixelPoints.length < 2) return;
|
|
13
|
+
|
|
14
|
+
const children: any[] = [];
|
|
15
|
+
|
|
16
|
+
// Fill triangle ABC
|
|
17
|
+
if (pixelPoints.length >= 3) {
|
|
18
|
+
children.push({
|
|
19
|
+
type: 'polygon',
|
|
20
|
+
name: 'line',
|
|
21
|
+
shape: { points: pixelPoints.slice(0, 3).map(([x, y]) => [x, y]) },
|
|
22
|
+
style: { fill: 'rgba(33, 150, 243, 0.08)' },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// Fill triangle BCD
|
|
26
|
+
if (pixelPoints.length >= 4) {
|
|
27
|
+
children.push({
|
|
28
|
+
type: 'polygon',
|
|
29
|
+
name: 'line',
|
|
30
|
+
shape: { points: pixelPoints.slice(1, 4).map(([x, y]) => [x, y]) },
|
|
31
|
+
style: { fill: 'rgba(244, 67, 54, 0.08)' },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Leg lines
|
|
36
|
+
for (let i = 0; i < pixelPoints.length - 1; i++) {
|
|
37
|
+
const [x1, y1] = pixelPoints[i];
|
|
38
|
+
const [x2, y2] = pixelPoints[i + 1];
|
|
39
|
+
children.push({
|
|
40
|
+
type: 'line',
|
|
41
|
+
name: 'line',
|
|
42
|
+
shape: { x1, y1, x2, y2 },
|
|
43
|
+
style: { stroke: LEG_COLORS[i % LEG_COLORS.length], lineWidth: drawing.style?.lineWidth || 2 },
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Dashed connector A→C
|
|
48
|
+
if (pixelPoints.length >= 3) {
|
|
49
|
+
children.push({
|
|
50
|
+
type: 'line',
|
|
51
|
+
shape: { x1: pixelPoints[0][0], y1: pixelPoints[0][1], x2: pixelPoints[2][0], y2: pixelPoints[2][1] },
|
|
52
|
+
style: { stroke: '#555', lineWidth: 1, lineDash: [4, 4] },
|
|
53
|
+
silent: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Dashed connector B→D
|
|
57
|
+
if (pixelPoints.length >= 4) {
|
|
58
|
+
children.push({
|
|
59
|
+
type: 'line',
|
|
60
|
+
shape: { x1: pixelPoints[1][0], y1: pixelPoints[1][1], x2: pixelPoints[3][0], y2: pixelPoints[3][1] },
|
|
61
|
+
style: { stroke: '#555', lineWidth: 1, lineDash: [4, 4] },
|
|
62
|
+
silent: true,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Fibonacci ratios
|
|
67
|
+
if (drawing.points.length >= 3) {
|
|
68
|
+
const ab = Math.abs(drawing.points[1].value - drawing.points[0].value);
|
|
69
|
+
const bc = Math.abs(drawing.points[2].value - drawing.points[1].value);
|
|
70
|
+
if (ab !== 0) {
|
|
71
|
+
const ratio = (bc / ab).toFixed(3);
|
|
72
|
+
const mx = (pixelPoints[1][0] + pixelPoints[2][0]) / 2;
|
|
73
|
+
const my = (pixelPoints[1][1] + pixelPoints[2][1]) / 2;
|
|
74
|
+
children.push({ type: 'text', style: { text: ratio, x: mx + 8, y: my, fill: '#ff9800', fontSize: 10 }, silent: true });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (drawing.points.length >= 4) {
|
|
78
|
+
const bc = Math.abs(drawing.points[2].value - drawing.points[1].value);
|
|
79
|
+
const cd = Math.abs(drawing.points[3].value - drawing.points[2].value);
|
|
80
|
+
if (bc !== 0) {
|
|
81
|
+
const ratio = (cd / bc).toFixed(3);
|
|
82
|
+
const mx = (pixelPoints[2][0] + pixelPoints[3][0]) / 2;
|
|
83
|
+
const my = (pixelPoints[2][1] + pixelPoints[3][1]) / 2;
|
|
84
|
+
children.push({ type: 'text', style: { text: ratio, x: mx + 8, y: my, fill: '#4caf50', fontSize: 10 }, silent: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Vertex labels
|
|
89
|
+
for (let i = 0; i < pixelPoints.length && i < LABELS.length; i++) {
|
|
90
|
+
const [px, py] = pixelPoints[i];
|
|
91
|
+
const isHigh = (i === 0 || py <= pixelPoints[i - 1][1]) && (i === pixelPoints.length - 1 || py <= pixelPoints[i + 1]?.[1]);
|
|
92
|
+
children.push({
|
|
93
|
+
type: 'text',
|
|
94
|
+
style: { text: LABELS[i], x: px, y: isHigh ? py - 14 : py + 16, fill: '#e2e8f0', fontSize: 12, fontWeight: 'bold', align: 'center', verticalAlign: 'middle' },
|
|
95
|
+
silent: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Control points
|
|
100
|
+
for (let i = 0; i < pixelPoints.length; i++) {
|
|
101
|
+
children.push({
|
|
102
|
+
type: 'circle',
|
|
103
|
+
name: `point-${i}`,
|
|
104
|
+
shape: { cx: pixelPoints[i][0], cy: pixelPoints[i][1], r: 4 },
|
|
105
|
+
style: { fill: '#fff', stroke: color, lineWidth: 1, opacity: isSelected ? 1 : 0 },
|
|
106
|
+
z: 100,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { type: 'group', children };
|
|
111
|
+
}
|
|
112
|
+
}
|