react-native-vroom-chart 0.6.0 → 0.8.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.
- package/cpp/VroomChartHostObject.cpp +281 -33
- package/cpp/_core_include/vroom/vroom_chart.h +231 -36
- package/cpp/_core_src/bollinger.h +1 -1
- package/cpp/_core_src/candles.cpp +138 -41
- package/cpp/_core_src/candles.h +11 -1
- package/cpp/_core_src/chart.cpp +91 -40
- package/cpp/_core_src/chart.h +69 -29
- package/cpp/_core_src/chart_facade.cpp +247 -69
- package/cpp/_core_src/drawings.cpp +147 -4
- package/cpp/_core_src/drawings.h +10 -5
- package/cpp/_core_src/gradient.cpp +46 -0
- package/cpp/_core_src/gradient.h +31 -0
- package/cpp/_core_src/labels.cpp +49 -16
- package/cpp/_core_src/labels.h +33 -4
- package/cpp/_core_src/liquidity.cpp +3 -37
- package/cpp/_core_src/ma_overlay.cpp +174 -12
- package/cpp/_core_src/ma_overlay.h +55 -3
- package/cpp/_core_src/macd.cpp +13 -42
- package/cpp/_core_src/macd.h +11 -8
- package/cpp/_core_src/macd_pane.cpp +57 -27
- package/cpp/_core_src/price_line_layout.h +1 -1
- package/cpp/_core_src/rsi.cpp +4 -18
- package/cpp/_core_src/rsi.h +6 -6
- package/cpp/_core_src/rsi_pane.cpp +34 -19
- package/cpp/_core_src/series_ma.cpp +64 -0
- package/cpp/_core_src/series_ma.h +30 -0
- package/cpp/_core_src/style_inherit.h +35 -0
- package/cpp/_core_src/theme.cpp +2 -1
- package/cpp/_core_src/viewport.cpp +36 -12
- package/cpp/_core_src/viewport.h +50 -0
- package/cpp/_core_src/volume.cpp +33 -8
- package/cpp/_core_src/volume.h +12 -1
- package/cpp/_core_src/volume_anim.cpp +32 -0
- package/cpp/_core_src/volume_anim.h +41 -0
- package/lib/index.d.mts +206 -31
- package/lib/index.d.ts +206 -31
- package/lib/index.js +324 -44
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +329 -52
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +100 -17
- package/src/dataTransitions.ts +148 -0
- package/src/easing.ts +40 -0
- package/src/index.ts +9 -0
- package/src/jsi.d.ts +126 -19
- package/src/theme.ts +1 -0
- package/src/types.ts +3 -0
- package/src/useChartCore.ts +273 -30
package/cpp/_core_src/chart.cpp
CHANGED
|
@@ -33,11 +33,16 @@
|
|
|
33
33
|
#include "price_lines.h"
|
|
34
34
|
#include "rsi.h"
|
|
35
35
|
#include "rsi_pane.h"
|
|
36
|
+
#include "style_inherit.h"
|
|
36
37
|
#include "volume.h"
|
|
37
38
|
#include "vwap.h"
|
|
38
39
|
#include "theme.h"
|
|
39
40
|
#include "viewport.h"
|
|
40
41
|
|
|
42
|
+
namespace {
|
|
43
|
+
constexpr SkColor kVwapLine = 0xff00bcd4; // cyan
|
|
44
|
+
} // namespace
|
|
45
|
+
|
|
41
46
|
VroomChart::VroomChart() : theme(vroom::default_theme()) {}
|
|
42
47
|
|
|
43
48
|
void VroomChart::mark_dirty() {
|
|
@@ -49,7 +54,7 @@ vroom::Layout VroomChart::layout() const {
|
|
|
49
54
|
const float axis_w = axis_width_px > 0.f
|
|
50
55
|
? axis_width_px
|
|
51
56
|
: width_px * theme.floats[VROOM_FLOAT_Y_AXIS_WIDTH_RATIO];
|
|
52
|
-
const int pane_count = (
|
|
57
|
+
const int pane_count = (rsi.enabled ? 1 : 0) + (macd.enabled ? 1 : 0);
|
|
53
58
|
const float indicator_h = static_cast<float>(pane_count) * height_px *
|
|
54
59
|
theme.floats[VROOM_FLOAT_INDICATOR_HEIGHT_FRAC];
|
|
55
60
|
return vroom::Layout{
|
|
@@ -66,10 +71,11 @@ vroom::Layout VroomChart::layout() const {
|
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
void VroomChart::ensure_rsi() {
|
|
69
|
-
if (!
|
|
70
|
-
vroom::rsi::compute(candles.data(), candles.size(),
|
|
71
|
-
if (
|
|
72
|
-
vroom::rsi::compute_ma(rsi_cache,
|
|
74
|
+
if (!rsi.enabled || !rsi_dirty) return;
|
|
75
|
+
vroom::rsi::compute(candles.data(), candles.size(), rsi.period, rsi_cache);
|
|
76
|
+
if (rsi.ma_visible) {
|
|
77
|
+
vroom::rsi::compute_ma(rsi_cache, rsi.ma_period, rsi.ma_kind,
|
|
78
|
+
rsi_ma_cache);
|
|
73
79
|
} else {
|
|
74
80
|
rsi_ma_cache.clear();
|
|
75
81
|
}
|
|
@@ -77,9 +83,10 @@ void VroomChart::ensure_rsi() {
|
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
void VroomChart::ensure_macd() {
|
|
80
|
-
if (!
|
|
81
|
-
vroom::macd::compute(candles.data(), candles.size(),
|
|
82
|
-
|
|
86
|
+
if (!macd.enabled || !macd_dirty) return;
|
|
87
|
+
vroom::macd::compute(candles.data(), candles.size(), macd.fast, macd.slow,
|
|
88
|
+
macd.signal, macd.source, macd.ma_kind,
|
|
89
|
+
macd.signal_ma_kind, macd_cache, macd_signal_cache,
|
|
83
90
|
macd_hist_cache);
|
|
84
91
|
macd_dirty = false;
|
|
85
92
|
}
|
|
@@ -96,8 +103,8 @@ void VroomChart::ensure_overlays() {
|
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
void VroomChart::ensure_vwap() {
|
|
99
|
-
if (!
|
|
100
|
-
vroom::vwap::compute(candles.data(), candles.size(),
|
|
106
|
+
if (!vwap.enabled || !vwap_dirty) return;
|
|
107
|
+
vroom::vwap::compute(candles.data(), candles.size(), vwap.reset_offset_min,
|
|
101
108
|
vwap_cache, vwap_breaks);
|
|
102
109
|
vwap_dirty = false;
|
|
103
110
|
}
|
|
@@ -112,6 +119,7 @@ void VroomChart::ensure_bollinger() {
|
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
122
|
+
begin_frame();
|
|
115
123
|
const auto lay = layout();
|
|
116
124
|
|
|
117
125
|
// 1. Background
|
|
@@ -140,19 +148,60 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
140
148
|
|
|
141
149
|
// 3. Update label fade state ONCE per frame — both gridlines and labels
|
|
142
150
|
// share these opacities so their animations stay in lockstep.
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
// While an interval morph fades the old ticks out, the axes are still laid
|
|
152
|
+
// out against the pre-switch scale and window so nothing appears to move;
|
|
153
|
+
// they adopt the new ones at the midpoint, invisible. Every axis call in
|
|
154
|
+
// the frame has to agree on which of the two it's using.
|
|
155
|
+
const auto axis_phase = vroom::labels::interval_phase(*this);
|
|
156
|
+
const auto& axis_bounds = axis_phase.outgoing ? morph_from_bounds : bounds;
|
|
157
|
+
const int64_t axis_start_ms =
|
|
158
|
+
axis_phase.outgoing ? morph_from_start_ms : visible_start_ms;
|
|
159
|
+
const int64_t axis_end_ms =
|
|
160
|
+
axis_phase.outgoing ? morph_from_end_ms : visible_end_ms;
|
|
161
|
+
vroom::labels::update_y_fades(*this, lay, axis_bounds);
|
|
162
|
+
vroom::labels::update_x_fades(*this, lay, axis_start_ms, axis_end_ms);
|
|
145
163
|
|
|
146
164
|
// 4. Gridlines — drawn before candles so candle bodies overlay them.
|
|
147
165
|
// Vertical (time) gridlines are intentionally disabled for now —
|
|
148
166
|
// re-enable by adding `vroom::labels::draw_x_gridlines(canvas, *this,
|
|
149
|
-
// candle_right, candle_area_h);` here.
|
|
150
|
-
vroom::labels::draw_y_gridlines(canvas, *this, lay,
|
|
167
|
+
// axis_start_ms, axis_end_ms, candle_right, candle_area_h);` here.
|
|
168
|
+
vroom::labels::draw_y_gridlines(canvas, *this, lay, axis_bounds,
|
|
151
169
|
candle_right, candle_area_h);
|
|
152
170
|
|
|
171
|
+
// 4.35. Price-series morph state, shared by the gradient fill below and the
|
|
172
|
+
// series itself (5). `morph_fade` crossfades candles→line and
|
|
173
|
+
// `morph_collapse` folds each candle toward its close (the line vertex);
|
|
174
|
+
// fade 0 = pure candles, fade 1 = pure line. An interval morph
|
|
175
|
+
// additionally reshapes each slot from the geometry it held before the
|
|
176
|
+
// timeframe switch (see `morph_from`) — candle bodies, line vertices and
|
|
177
|
+
// the fill alike, so every layer stays in step mid-crossfade.
|
|
178
|
+
const float fade = morph_fade;
|
|
179
|
+
const float collapse = morph_collapse;
|
|
180
|
+
const bool morphing = interval_morph_t < 1.f && !morph_from.empty();
|
|
181
|
+
const vroom::CandleSnapshot* morph_src = morphing ? morph_from.data() : nullptr;
|
|
182
|
+
const std::size_t morph_n = morphing ? morph_from.size() : 0;
|
|
183
|
+
const float morph_t = morphing ? interval_morph_t : 1.f;
|
|
184
|
+
|
|
185
|
+
// 4.4. Line-mode gradient fill — the wash under the close polyline. Behind the
|
|
186
|
+
// volume bars so they stay legible on top of it, which puts it at the
|
|
187
|
+
// back of the price pane's content.
|
|
188
|
+
if (fade > 0.f) {
|
|
189
|
+
vroom::ma_overlay::draw_close_gradient(
|
|
190
|
+
canvas, lay, bounds, visible, n, window_ms,
|
|
191
|
+
visible_start_ms, candle_duration_ms, candle_right, candle_area_h,
|
|
192
|
+
theme.colors[VROOM_COLOR_LINE],
|
|
193
|
+
theme.floats[VROOM_FLOAT_LINE_GRADIENT_OPACITY],
|
|
194
|
+
fade, morph_src, morph_n, morph_t);
|
|
195
|
+
}
|
|
196
|
+
|
|
153
197
|
// 4.5. Volume bars — drawn under the candles so candles z-index above.
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
// A fully collapsed chart has no bars left to draw, which is also how
|
|
199
|
+
// "volume disabled" is represented (set_volume snaps the scalar).
|
|
200
|
+
if (volume_collapse_t < 1.f) {
|
|
201
|
+
vroom::volume::draw(canvas, visible, n, lay, theme, volume,
|
|
202
|
+
volume_collapse_t, volume_collapse_easing,
|
|
203
|
+
window_ms, visible_start_ms, candle_duration_ms);
|
|
204
|
+
}
|
|
156
205
|
|
|
157
206
|
// 4.6. Liquidity bands (resting-order depth) — behind the candles so the
|
|
158
207
|
// candle bodies paint over the volume-driven tint. Anchored in price
|
|
@@ -178,25 +227,19 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
178
227
|
}
|
|
179
228
|
|
|
180
229
|
// 5. Price series — candles, a close-price line, or a blend of the two during
|
|
181
|
-
// the candle↔line morph.
|
|
182
|
-
//
|
|
183
|
-
// fade 0 = pure candles, fade 1 = pure line. The line reuses the MA-overlay
|
|
184
|
-
// polyline routine, fed the visible closes and styled by theme.LINE.
|
|
185
|
-
const float fade = morph_fade;
|
|
186
|
-
const float collapse = morph_collapse;
|
|
230
|
+
// the candle↔line morph (state hoisted to 4.35 for the gradient fill). The
|
|
231
|
+
// line is styled by theme.LINE.
|
|
187
232
|
if (fade < 1.f) {
|
|
188
233
|
vroom::candles::draw(canvas, visible, n, lay, theme, bounds, window_ms,
|
|
189
234
|
visible_start_ms, candle_duration_ms, collapse,
|
|
190
|
-
1.f - fade);
|
|
235
|
+
1.f - fade, morph_src, morph_n, morph_t);
|
|
191
236
|
}
|
|
192
237
|
if (fade > 0.f) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
vroom::ma_overlay::draw(
|
|
196
|
-
canvas, lay, bounds, visible, n, closes.data(), window_ms,
|
|
238
|
+
vroom::ma_overlay::draw_close_line(
|
|
239
|
+
canvas, lay, bounds, visible, n, window_ms,
|
|
197
240
|
visible_start_ms, candle_duration_ms, candle_right, candle_area_h,
|
|
198
241
|
theme.colors[VROOM_COLOR_LINE], theme.floats[VROOM_FLOAT_LINE_WIDTH_PX],
|
|
199
|
-
|
|
242
|
+
fade, morph_src, morph_n, morph_t);
|
|
200
243
|
}
|
|
201
244
|
|
|
202
245
|
// 5.5. Moving-average overlay lines (SMA/EMA) on the price pane, over the
|
|
@@ -216,7 +259,7 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
216
259
|
|
|
217
260
|
// 5.6. VWAP overlay (session, configurable reset) — a single price-pane line
|
|
218
261
|
// that breaks at each session reset (vwap_breaks).
|
|
219
|
-
if (
|
|
262
|
+
if (vwap.enabled) {
|
|
220
263
|
ensure_vwap();
|
|
221
264
|
if (vwap_cache.size() == candles.size()) {
|
|
222
265
|
const double* vis = vwap_cache.data() + range.start;
|
|
@@ -226,7 +269,10 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
226
269
|
vroom::ma_overlay::draw(canvas, lay, bounds, visible, n, vis,
|
|
227
270
|
window_ms, visible_start_ms,
|
|
228
271
|
candle_duration_ms, candle_right,
|
|
229
|
-
candle_area_h,
|
|
272
|
+
candle_area_h,
|
|
273
|
+
vroom::style::color_or(vwap.color, kVwapLine),
|
|
274
|
+
vroom::style::width_or(vwap.width, 1.5f),
|
|
275
|
+
brk);
|
|
230
276
|
}
|
|
231
277
|
}
|
|
232
278
|
|
|
@@ -272,8 +318,8 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
272
318
|
axis_bg);
|
|
273
319
|
|
|
274
320
|
// 7. Labels (read from y_fades / x_fades, no state mutation here)
|
|
275
|
-
vroom::labels::draw_y_labels(canvas, *this, lay,
|
|
276
|
-
vroom::labels::draw_x_labels(canvas, *this, lay);
|
|
321
|
+
vroom::labels::draw_y_labels(canvas, *this, lay, axis_bounds);
|
|
322
|
+
vroom::labels::draw_x_labels(canvas, *this, lay, axis_start_ms, axis_end_ms);
|
|
277
323
|
|
|
278
324
|
// 7.5. Current-price line + box — above labels so the box covers any label
|
|
279
325
|
// it overlaps; tracks the latest close as the price scale moves.
|
|
@@ -294,8 +340,8 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
294
340
|
struct ActivePane { int order; int type; }; // type: 0 = RSI, 1 = MACD
|
|
295
341
|
ActivePane panes[2];
|
|
296
342
|
int count = 0;
|
|
297
|
-
if (
|
|
298
|
-
if (
|
|
343
|
+
if (rsi.enabled) panes[count++] = {rsi_order, 0};
|
|
344
|
+
if (macd.enabled) panes[count++] = {macd_order, 1};
|
|
299
345
|
if (count == 2 && panes[0].order > panes[1].order) {
|
|
300
346
|
const ActivePane tmp = panes[0];
|
|
301
347
|
panes[0] = panes[1];
|
|
@@ -312,7 +358,7 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
312
358
|
const double* rsi_vis = rsi_cache.size() == candles.size()
|
|
313
359
|
? rsi_cache.data() + range.start : nullptr;
|
|
314
360
|
const double* rsi_ma_vis =
|
|
315
|
-
(
|
|
361
|
+
(rsi.ma_visible && rsi_ma_cache.size() == candles.size())
|
|
316
362
|
? rsi_ma_cache.data() + range.start : nullptr;
|
|
317
363
|
vroom::rsi_pane::draw(canvas, *this, lay, visible, n, rsi_vis,
|
|
318
364
|
rsi_ma_vis, window_ms, visible_start_ms,
|
|
@@ -361,19 +407,24 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
361
407
|
vroom::labels::gc_x_fades(*this);
|
|
362
408
|
}
|
|
363
409
|
|
|
364
|
-
void VroomChart::
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
|
|
410
|
+
void VroomChart::begin_frame() {
|
|
411
|
+
// dt for the fade animations. A large gap (resume from background, or an idle
|
|
412
|
+
// chart that a click just woke up) is clamped to a nominal frame rather than
|
|
413
|
+
// zeroed: dt == 0 means "snap" to the fade updaters, which would finish every
|
|
414
|
+
// fade in the first frame after any idle period.
|
|
415
|
+
constexpr float kNominalFrameSeconds = 1.f / 60.f;
|
|
416
|
+
const auto now = std::chrono::steady_clock::now();
|
|
368
417
|
float dt = 0.f;
|
|
369
418
|
if (anim_started) {
|
|
370
419
|
dt = std::chrono::duration<float>(now - last_anim_tick).count();
|
|
371
|
-
if (dt > 0.1f) dt =
|
|
420
|
+
if (dt > 0.1f) dt = kNominalFrameSeconds;
|
|
372
421
|
}
|
|
373
422
|
last_anim_tick = now;
|
|
374
423
|
anim_started = true;
|
|
375
424
|
last_dt_seconds = dt;
|
|
425
|
+
}
|
|
376
426
|
|
|
427
|
+
void VroomChart::rebuild_chart_picture() {
|
|
377
428
|
SkPictureRecorder recorder;
|
|
378
429
|
SkCanvas* canvas = recorder.beginRecording(SkRect::MakeWH(width_px, height_px));
|
|
379
430
|
draw_chart(canvas);
|
package/cpp/_core_src/chart.h
CHANGED
|
@@ -64,6 +64,21 @@ struct VroomChart {
|
|
|
64
64
|
float morph_collapse = 0.f;
|
|
65
65
|
float morph_fade = 0.f;
|
|
66
66
|
|
|
67
|
+
// Interval morph: the outgoing candle geometry captured when a timeframe
|
|
68
|
+
// switch begins, indexed from the right of the visible slice (slot 0 =
|
|
69
|
+
// newest) — the pairing the preserved slot grid guarantees. Stored as
|
|
70
|
+
// normalized fractions so it survives the new bounds and a resize.
|
|
71
|
+
// Empty when not morphing.
|
|
72
|
+
std::vector<vroom::CandleSnapshot> morph_from;
|
|
73
|
+
float interval_morph_t = 1.f; // 1 = not morphing
|
|
74
|
+
// The pre-switch price scale and time window. The candle capture above is
|
|
75
|
+
// normalized against these, and the axes keep rendering their old ticks from
|
|
76
|
+
// them while fading out (see labels::interval_phase), so a label is never
|
|
77
|
+
// drawn at a position it didn't already occupy.
|
|
78
|
+
vroom::PriceBounds morph_from_bounds{0.0, 1.0};
|
|
79
|
+
int64_t morph_from_start_ms = 0;
|
|
80
|
+
int64_t morph_from_end_ms = 0;
|
|
81
|
+
|
|
67
82
|
// Cached y-axis width in pixels, sized to fit the widest formatted price
|
|
68
83
|
// label. 0 = uncomputed; layout() falls back to a width ratio.
|
|
69
84
|
float axis_width_px = 0.f;
|
|
@@ -86,24 +101,27 @@ struct VroomChart {
|
|
|
86
101
|
// RSI, drawn in a pane below the candles. rsi_cache is aligned to `candles`
|
|
87
102
|
// (one value per candle, NaN where undefined) and recomputed lazily by
|
|
88
103
|
// ensure_rsi() when rsi_dirty (set on any data or config change).
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
// Defaults: 14-period RSI with 70/30 bands and a 14-period SMA trendline,
|
|
105
|
+
// every style field on its inherit sentinel.
|
|
106
|
+
VroomRSI rsi{0, 14, 70.0, 30.0, 14, 0, 1,
|
|
107
|
+
0u, -1.f, 1, // RSI line
|
|
108
|
+
0u, -1.f, // trendline
|
|
109
|
+
0u, 1}; // band rules
|
|
95
110
|
std::vector<double> rsi_cache; // RSI per candle (NaN where undefined)
|
|
96
|
-
std::vector<double> rsi_ma_cache; //
|
|
111
|
+
std::vector<double> rsi_ma_cache; // MA of rsi_cache (empty if MA off)
|
|
97
112
|
bool rsi_dirty = true;
|
|
98
113
|
// User y-axis zoom for the RSI pane (drag on its y-axis strip). 1.0 = the
|
|
99
114
|
// default 0..100 fit; >1 zooms in (taller), <1 zooms out. Anchored at 50.
|
|
100
115
|
double rsi_y_scale = 1.0;
|
|
101
116
|
|
|
102
117
|
// MACD, drawn in its own pane. Caches aligned to `candles` (NaN warmup).
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
// Defaults: 12/26/9 EMAs of close, every style field on its inherit
|
|
119
|
+
// sentinel so an untouched chart keeps the stock look.
|
|
120
|
+
VroomMACD macd{0, 12, 26, 9, 0, 1, 1,
|
|
121
|
+
0u, -1.f, 1, // MACD line
|
|
122
|
+
0u, -1.f, 1, // signal line
|
|
123
|
+
1, 0u, 0u, 0u, 0u, // histogram
|
|
124
|
+
0u, 1}; // zero line
|
|
107
125
|
std::vector<double> macd_cache;
|
|
108
126
|
std::vector<double> macd_signal_cache;
|
|
109
127
|
std::vector<double> macd_hist_cache;
|
|
@@ -128,10 +146,8 @@ struct VroomChart {
|
|
|
128
146
|
|
|
129
147
|
// VWAP overlay (session anchor, configurable reset time). Single line on the
|
|
130
148
|
// price pane; vwap_breaks marks session resets so the line lifts the pen.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
uint32_t vwap_color = 0xff00bcd4; // cyan
|
|
134
|
-
float vwap_width = 1.5f;
|
|
149
|
+
// Style fields sit on their inherit sentinel (cyan, 1.5px).
|
|
150
|
+
VroomVWAP vwap{0, 0, 0u, -1.f};
|
|
135
151
|
std::vector<double> vwap_cache;
|
|
136
152
|
std::vector<unsigned char> vwap_breaks;
|
|
137
153
|
bool vwap_dirty = true;
|
|
@@ -150,13 +166,26 @@ struct VroomChart {
|
|
|
150
166
|
std::vector<double> bb_lower_cache;
|
|
151
167
|
bool bollinger_dirty = true;
|
|
152
168
|
|
|
169
|
+
// Volume bars (price pane, under the candles). On by default with every
|
|
170
|
+
// style field left on its inherit sentinel, so an untouched chart looks
|
|
171
|
+
// exactly as it did before the config existed. No cache — bar heights come
|
|
172
|
+
// straight off the visible candles' volume.
|
|
173
|
+
VroomVolume volume{1, -1.f, -1.f, -1.f, 0u, 0u};
|
|
174
|
+
// Staggered collapse of those bars, driven per-frame by the host animation
|
|
175
|
+
// loop: 0 = full height, 1 = all flat. Doubles as the visibility gate, since
|
|
176
|
+
// a fully collapsed chart draws nothing — set_volume snaps it to match
|
|
177
|
+
// `volume.enabled`, and the host overrides it while animating.
|
|
178
|
+
float volume_collapse_t = 0.f;
|
|
179
|
+
int32_t volume_collapse_easing = VROOM_EASING_IN_OUT;
|
|
180
|
+
|
|
153
181
|
// --- drawings (annotations) --------------------------------------------
|
|
154
182
|
// Committed drawings, anchored in data space so they track the candles on
|
|
155
183
|
// pan/zoom. Drawn on the price pane above candles/overlays.
|
|
156
184
|
//
|
|
157
|
-
// Mirrors the public VroomDrawing but *owns* its points, so a pencil
|
|
158
|
-
// (kind 2) can carry a variable number of them. For
|
|
159
|
-
// empty and only a/b are used; for pencil a/b
|
|
185
|
+
// Mirrors the public VroomDrawing but *owns* its points, so a pencil stroke
|
|
186
|
+
// (kind 2) or path (kind 3) can carry a variable number of them. For
|
|
187
|
+
// line/box `points` is empty and only a/b are used; for pencil/path a/b
|
|
188
|
+
// mirror the first/last point.
|
|
160
189
|
struct StoredDrawing {
|
|
161
190
|
VroomDrawPoint a{};
|
|
162
191
|
VroomDrawPoint b{};
|
|
@@ -171,7 +200,9 @@ struct VroomChart {
|
|
|
171
200
|
// draft_a is always drawn (node dot); draft_b is drawn when draft_has_b.
|
|
172
201
|
// draft_guide draws the live guideline A->B; when false only node dots show
|
|
173
202
|
// (the committed segment renders via `drawings`). draft_color/draft_width
|
|
174
|
-
// style the guideline to match the eventual line.
|
|
203
|
+
// style the guideline to match the eventual line. For a path (draft_kind 3)
|
|
204
|
+
// draft_points holds the vertices placed so far and draft_b is the cursor
|
|
205
|
+
// the rubber-band segment runs to (when draft_has_b).
|
|
175
206
|
bool draft_active = false;
|
|
176
207
|
VroomDrawPoint draft_a{};
|
|
177
208
|
bool draft_has_b = false;
|
|
@@ -179,14 +210,16 @@ struct VroomChart {
|
|
|
179
210
|
bool draft_guide = false;
|
|
180
211
|
uint32_t draft_color = 0xff2962ff;
|
|
181
212
|
float draft_width = 2.f;
|
|
182
|
-
int32_t draft_kind = 0; // 0 = line, 1 = box, 2 = pencil
|
|
183
|
-
//
|
|
213
|
+
int32_t draft_kind = 0; // 0 = line, 1 = box, 2 = pencil, 3 = path
|
|
214
|
+
// Points of the in-progress stroke (draft_kind 2, grown one at a time) or
|
|
215
|
+
// path (draft_kind 3, restated per click/move).
|
|
184
216
|
std::vector<VroomDrawPoint> draft_points;
|
|
185
217
|
|
|
186
218
|
// Selection/editing state for committed drawings. selected_drawing indexes
|
|
187
|
-
// `drawings` (or -1); its
|
|
188
|
-
// 0 (A) or 1 (B)
|
|
189
|
-
//
|
|
219
|
+
// `drawings` (or -1); its handles render on top. grabbed_endpoint is the
|
|
220
|
+
// handle being dragged — 0 (A) or 1 (B) for a line, 0 for a box (the host
|
|
221
|
+
// normalizes the grabbed corner to endpoint 0), the vertex index for a path
|
|
222
|
+
// — rendered 50% larger; -1 when none.
|
|
190
223
|
int32_t selected_drawing = -1;
|
|
191
224
|
int32_t grabbed_endpoint = -1;
|
|
192
225
|
|
|
@@ -235,9 +268,9 @@ struct VroomChart {
|
|
|
235
268
|
std::chrono::steady_clock::time_point last_anim_tick{};
|
|
236
269
|
bool anim_started = false;
|
|
237
270
|
|
|
238
|
-
// dt of the current
|
|
239
|
-
//
|
|
240
|
-
//
|
|
271
|
+
// dt of the current frame — populated by begin_frame and consumed by the
|
|
272
|
+
// label-fade updaters. Public so the labels namespace can read it without a
|
|
273
|
+
// getter.
|
|
241
274
|
float last_dt_seconds = 0.f;
|
|
242
275
|
|
|
243
276
|
// --- methods ------------------------------------------------------------
|
|
@@ -267,11 +300,18 @@ struct VroomChart {
|
|
|
267
300
|
// indicator is enabled.
|
|
268
301
|
void ensure_bollinger();
|
|
269
302
|
|
|
270
|
-
// The main drawing pass. Calls into the labels and candles modules
|
|
303
|
+
// The main drawing pass. Calls into the labels and candles modules, and owns
|
|
304
|
+
// the per-frame animation clock (see begin_frame) so every host gets it —
|
|
305
|
+
// the web build draws straight through vroom_chart_draw rather than the
|
|
306
|
+
// SkPicture cache below.
|
|
271
307
|
void draw_chart(SkCanvas* canvas);
|
|
272
308
|
|
|
309
|
+
// Per-frame animation bookkeeping: computes `last_dt_seconds`, which paces
|
|
310
|
+
// the label fades. Called at the top of draw_chart.
|
|
311
|
+
void begin_frame();
|
|
312
|
+
|
|
273
313
|
// Re-records `chart_picture` by invoking draw_chart on a fresh
|
|
274
|
-
// SkPictureRecorder.
|
|
314
|
+
// SkPictureRecorder.
|
|
275
315
|
void rebuild_chart_picture();
|
|
276
316
|
|
|
277
317
|
// True if any axis label is mid-fade. Used by the JS-side animation loop
|