react-native-vroom-chart 0.13.0 → 0.14.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/android/CMakeLists.txt +8 -0
- package/android/README.md +11 -10
- package/cpp/VroomChartHostObject.cpp +155 -54
- package/cpp/VroomChartHostObject.h +1 -1
- package/cpp/VroomJsiInstaller.cpp +1 -1
- package/cpp/_core_include/vroom/vroom_chart.h +6 -7
- package/cpp/_core_src/candles.h +7 -5
- package/cpp/_core_src/chart.cpp +271 -152
- package/cpp/_core_src/chart.h +6 -3
- package/cpp/_core_src/chart_facade.cpp +5 -1
- package/cpp/_core_src/viewport.h +4 -0
- package/lib/index.d.mts +52 -6
- package/lib/index.d.ts +52 -6
- package/lib/index.js +68 -36
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +77 -38
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +94 -44
- package/src/index.ts +2 -0
- package/src/jsi.d.ts +18 -17
- package/src/types.ts +2 -0
- package/src/useChartCore.ts +11 -7
package/cpp/_core_src/chart.cpp
CHANGED
|
@@ -178,160 +178,274 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
178
178
|
// 4.35. Price-series morph state, shared by the gradient fill below and the
|
|
179
179
|
// series itself (5). `morph_fade` crossfades candles→line and
|
|
180
180
|
// `morph_collapse` folds each candle toward its close (the line vertex);
|
|
181
|
-
// fade 0 = pure candles, fade 1 = pure line.
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
// the
|
|
181
|
+
// fade 0 = pure candles, fade 1 = pure line.
|
|
182
|
+
//
|
|
183
|
+
// An interval *transform* reshapes each slot from the geometry it held
|
|
184
|
+
// before the timeframe switch (see `morph_from`). A host-chosen
|
|
185
|
+
// *fade* skips the pairing and uses the same envelope as the axes
|
|
186
|
+
// (`labels::interval_phase`): outgoing snapshot out, then new scene in.
|
|
185
187
|
const float fade = morph_fade;
|
|
186
188
|
const float collapse = morph_collapse;
|
|
187
189
|
const bool morphing = interval_morph_t < 1.f && !morph_from.empty();
|
|
188
|
-
const vroom::CandleSnapshot* morph_src = morphing ? morph_from.data() : nullptr;
|
|
189
190
|
const std::size_t morph_n = morphing ? morph_from.size() : 0;
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
191
|
+
const bool fade_swap = morphing && interval_morph_fade;
|
|
192
|
+
const bool fade_out = fade_swap && axis_phase.outgoing;
|
|
193
|
+
const bool fade_in = fade_swap && !axis_phase.outgoing;
|
|
194
|
+
const bool reshape = morphing && !fade_swap;
|
|
195
|
+
const vroom::CandleSnapshot* morph_src = reshape ? morph_from.data() : nullptr;
|
|
196
|
+
const std::size_t morph_n_draw = reshape ? morph_n : 0;
|
|
197
|
+
const float morph_t = reshape ? interval_morph_t : 1.f;
|
|
198
|
+
|
|
199
|
+
if (fade_out) {
|
|
200
|
+
// First half: only the captured series, at the axis envelope opacity.
|
|
201
|
+
// Layers with no snapshot (volume, overlays, indicators, drawings)
|
|
202
|
+
// would already be the new data, so they stay hidden until the incoming
|
|
203
|
+
// half. morph_t = 0 draws the capture pixel-identically.
|
|
204
|
+
const float layer = axis_phase.opacity;
|
|
205
|
+
if (layer > 0.f) {
|
|
206
|
+
if (fade > 0.f) {
|
|
207
|
+
vroom::ma_overlay::draw_close_gradient(
|
|
208
|
+
canvas, lay, bounds, visible, 0, window_ms,
|
|
209
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
210
|
+
candle_area_h, theme.colors[VROOM_COLOR_LINE],
|
|
211
|
+
theme.floats[VROOM_FLOAT_LINE_GRADIENT_OPACITY],
|
|
212
|
+
fade * layer, morph_from.data(), morph_n, 0.f,
|
|
213
|
+
theme.floats[VROOM_FLOAT_LINE_TENSION]);
|
|
214
|
+
}
|
|
215
|
+
if (fade < 1.f) {
|
|
216
|
+
vroom::candles::draw(canvas, visible, 0, lay, theme, bounds,
|
|
217
|
+
window_ms, visible_start_ms,
|
|
218
|
+
candle_duration_ms, collapse,
|
|
219
|
+
(1.f - fade) * layer, morph_from.data(),
|
|
220
|
+
morph_n, 0.f);
|
|
221
|
+
}
|
|
222
|
+
if (fade > 0.f) {
|
|
223
|
+
vroom::ma_overlay::draw_close_line(
|
|
224
|
+
canvas, lay, bounds, visible, 0, window_ms,
|
|
225
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
226
|
+
candle_area_h, theme.colors[VROOM_COLOR_LINE],
|
|
227
|
+
theme.floats[VROOM_FLOAT_LINE_WIDTH_PX], fade * layer,
|
|
228
|
+
morph_from.data(), morph_n, 0.f,
|
|
229
|
+
theme.floats[VROOM_FLOAT_LINE_TENSION]);
|
|
230
|
+
if (theme.floats[VROOM_FLOAT_LINE_TIP_DOT] > 0.5f) {
|
|
231
|
+
vroom::ma_overlay::draw_close_tip(
|
|
232
|
+
canvas, lay, bounds, visible, 0, window_ms,
|
|
233
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
234
|
+
candle_area_h, theme.colors[VROOM_COLOR_LINE],
|
|
235
|
+
theme.colors[VROOM_COLOR_BACKGROUND],
|
|
236
|
+
theme.floats[VROOM_FLOAT_LINE_WIDTH_PX], fade * layer,
|
|
237
|
+
theme.floats[VROOM_FLOAT_LINE_TIP_PULSE] > 0.5f,
|
|
238
|
+
tip_pulse_elapsed_s / vroom::tip_pulse::kPeriodSeconds,
|
|
239
|
+
morph_from.data(), morph_n, 0.f);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
// Incoming fade: the new scene at envelope opacity, no slot lerp
|
|
245
|
+
// (`morph_t` is already 1). Transforms use the reshape path above.
|
|
246
|
+
const bool wrap = fade_in && axis_phase.opacity > 0.f &&
|
|
247
|
+
axis_phase.opacity < 0.999f;
|
|
248
|
+
if (wrap) {
|
|
249
|
+
canvas->saveLayerAlpha(
|
|
250
|
+
nullptr, static_cast<U8CPU>(axis_phase.opacity * 255.f + 0.5f));
|
|
251
|
+
}
|
|
252
|
+
const bool draw_new = !fade_in || axis_phase.opacity > 0.f;
|
|
253
|
+
|
|
254
|
+
if (draw_new) {
|
|
255
|
+
// 4.4. Line-mode gradient fill — the wash under the close polyline.
|
|
256
|
+
if (fade > 0.f) {
|
|
257
|
+
vroom::ma_overlay::draw_close_gradient(
|
|
258
|
+
canvas, lay, bounds, visible, n, window_ms,
|
|
259
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
260
|
+
candle_area_h, theme.colors[VROOM_COLOR_LINE],
|
|
261
|
+
theme.floats[VROOM_FLOAT_LINE_GRADIENT_OPACITY], fade,
|
|
262
|
+
morph_src, morph_n_draw, morph_t,
|
|
263
|
+
theme.floats[VROOM_FLOAT_LINE_TENSION]);
|
|
264
|
+
}
|
|
204
265
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
266
|
+
// 4.5. Volume bars — drawn under the candles so candles z-index above.
|
|
267
|
+
if (volume_collapse_t < 1.f) {
|
|
268
|
+
vroom::volume::draw(canvas, visible, n, lay, theme, volume,
|
|
269
|
+
volume_collapse_t, volume_collapse_easing,
|
|
270
|
+
window_ms, visible_start_ms,
|
|
271
|
+
candle_duration_ms);
|
|
272
|
+
}
|
|
213
273
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
candle_area_h, bollinger.upper_color, bollinger.fill_opacity);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
274
|
+
// 4.6. Liquidity bands (resting-order depth).
|
|
275
|
+
vroom::liquidity::draw(canvas, *this, lay, bounds, candle_right,
|
|
276
|
+
candle_area_h);
|
|
277
|
+
|
|
278
|
+
// 4.7. Bollinger Band fill.
|
|
279
|
+
if (bollinger.enabled) {
|
|
280
|
+
ensure_bollinger();
|
|
281
|
+
if (bollinger.fill_enabled &&
|
|
282
|
+
bb_upper_cache.size() == candles.size() &&
|
|
283
|
+
bb_lower_cache.size() == candles.size()) {
|
|
284
|
+
vroom::ma_overlay::fill_between(
|
|
285
|
+
canvas, lay, bounds, visible, n,
|
|
286
|
+
bb_upper_cache.data() + range.start,
|
|
287
|
+
bb_lower_cache.data() + range.start, window_ms,
|
|
288
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
289
|
+
candle_area_h, bollinger.upper_color,
|
|
290
|
+
bollinger.fill_opacity);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
236
293
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
294
|
+
// 5. Price series — candles, a close-price line, or a blend.
|
|
295
|
+
if (fade < 1.f) {
|
|
296
|
+
vroom::candles::draw(canvas, visible, n, lay, theme, bounds,
|
|
297
|
+
window_ms, visible_start_ms,
|
|
298
|
+
candle_duration_ms, collapse, 1.f - fade,
|
|
299
|
+
morph_src, morph_n_draw, morph_t);
|
|
300
|
+
}
|
|
301
|
+
if (fade > 0.f) {
|
|
302
|
+
vroom::ma_overlay::draw_close_line(
|
|
303
|
+
canvas, lay, bounds, visible, n, window_ms,
|
|
304
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
305
|
+
candle_area_h, theme.colors[VROOM_COLOR_LINE],
|
|
306
|
+
theme.floats[VROOM_FLOAT_LINE_WIDTH_PX], fade, morph_src,
|
|
307
|
+
morph_n_draw, morph_t,
|
|
308
|
+
theme.floats[VROOM_FLOAT_LINE_TENSION]);
|
|
309
|
+
}
|
|
253
310
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
}
|
|
311
|
+
// 5.5. Moving-average overlay lines.
|
|
312
|
+
if (!overlays.empty()) {
|
|
313
|
+
ensure_overlays();
|
|
314
|
+
for (std::size_t k = 0; k < overlays.size(); ++k) {
|
|
315
|
+
if (overlay_caches[k].size() != candles.size()) continue;
|
|
316
|
+
const double* vis = overlay_caches[k].data() + range.start;
|
|
317
|
+
vroom::ma_overlay::draw(
|
|
318
|
+
canvas, lay, bounds, visible, n, vis, window_ms,
|
|
319
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
320
|
+
candle_area_h, overlays[k].color, overlays[k].width);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
268
323
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
324
|
+
// 5.6. VWAP overlay.
|
|
325
|
+
if (vwap.enabled) {
|
|
326
|
+
ensure_vwap();
|
|
327
|
+
if (vwap_cache.size() == candles.size()) {
|
|
328
|
+
const double* vis = vwap_cache.data() + range.start;
|
|
329
|
+
const unsigned char* brk =
|
|
330
|
+
vwap_breaks.size() == candles.size()
|
|
331
|
+
? vwap_breaks.data() + range.start
|
|
332
|
+
: nullptr;
|
|
333
|
+
vroom::ma_overlay::draw(
|
|
334
|
+
canvas, lay, bounds, visible, n, vis, window_ms,
|
|
335
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
336
|
+
candle_area_h,
|
|
337
|
+
vroom::style::color_or(vwap.color, kVwapLine),
|
|
338
|
+
vroom::style::width_or(vwap.width, 1.5f), brk);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
287
341
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
342
|
+
// 5.65. Bollinger Band lines.
|
|
343
|
+
if (bollinger.enabled) {
|
|
344
|
+
ensure_bollinger();
|
|
345
|
+
const std::size_t sz = candles.size();
|
|
346
|
+
if (bb_upper_cache.size() == sz && bb_lower_cache.size() == sz &&
|
|
347
|
+
bb_middle_cache.size() == sz) {
|
|
348
|
+
const auto stroke = [&](const std::vector<double>& cache,
|
|
349
|
+
uint32_t color, float width) {
|
|
350
|
+
vroom::ma_overlay::draw(
|
|
351
|
+
canvas, lay, bounds, visible, n,
|
|
352
|
+
cache.data() + range.start, window_ms,
|
|
353
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
354
|
+
candle_area_h, color, width);
|
|
355
|
+
};
|
|
356
|
+
stroke(bb_upper_cache, bollinger.upper_color,
|
|
357
|
+
bollinger.upper_width);
|
|
358
|
+
stroke(bb_lower_cache, bollinger.lower_color,
|
|
359
|
+
bollinger.lower_width);
|
|
360
|
+
stroke(bb_middle_cache, bollinger.middle_color,
|
|
361
|
+
bollinger.middle_width);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
309
364
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
365
|
+
// 5.7. Drawing annotations.
|
|
366
|
+
vroom::drawings::draw(canvas, *this, lay, bounds, candle_right,
|
|
367
|
+
candle_area_h);
|
|
368
|
+
|
|
369
|
+
// 5.8. Line-mode tip marker.
|
|
370
|
+
if (fade > 0.f && theme.floats[VROOM_FLOAT_LINE_TIP_DOT] > 0.5f) {
|
|
371
|
+
const auto anchor = vroom::tip_anchor::at(
|
|
372
|
+
range.start, range.end, candles.size(), reshape);
|
|
373
|
+
vroom::ma_overlay::draw_close_tip(
|
|
374
|
+
canvas, lay, bounds, visible, anchor.slot_count, window_ms,
|
|
375
|
+
visible_start_ms, candle_duration_ms, candle_right,
|
|
376
|
+
candle_area_h, theme.colors[VROOM_COLOR_LINE],
|
|
377
|
+
theme.colors[VROOM_COLOR_BACKGROUND],
|
|
378
|
+
theme.floats[VROOM_FLOAT_LINE_WIDTH_PX], fade,
|
|
379
|
+
theme.floats[VROOM_FLOAT_LINE_TIP_PULSE] > 0.5f,
|
|
380
|
+
tip_pulse_elapsed_s / vroom::tip_pulse::kPeriodSeconds,
|
|
381
|
+
anchor.use_morph ? morph_src : nullptr,
|
|
382
|
+
anchor.use_morph ? morph_n_draw : 0, morph_t);
|
|
383
|
+
}
|
|
314
384
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
385
|
+
// Incoming fade: indicator panes share the scene opacity (and
|
|
386
|
+
// paint before axis masks so overflow still clips). Transforms
|
|
387
|
+
// keep the settled z-order after the price indicator.
|
|
388
|
+
if (fade_in && lay.indicator_area_h > 0.f) {
|
|
389
|
+
struct ActivePane {
|
|
390
|
+
int order;
|
|
391
|
+
int type;
|
|
392
|
+
};
|
|
393
|
+
ActivePane panes[2];
|
|
394
|
+
int count = 0;
|
|
395
|
+
if (rsi.enabled) panes[count++] = {rsi_order, 0};
|
|
396
|
+
if (macd.enabled) panes[count++] = {macd_order, 1};
|
|
397
|
+
if (count == 2 && panes[0].order > panes[1].order) {
|
|
398
|
+
const ActivePane tmp = panes[0];
|
|
399
|
+
panes[0] = panes[1];
|
|
400
|
+
panes[1] = tmp;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const float pane_h =
|
|
404
|
+
height_px * theme.floats[VROOM_FLOAT_INDICATOR_HEIGHT_FRAC];
|
|
405
|
+
float pane_top = candle_area_h;
|
|
406
|
+
for (int i = 0; i < count; ++i) {
|
|
407
|
+
const float pane_bottom = pane_top + pane_h;
|
|
408
|
+
if (panes[i].type == 0) {
|
|
409
|
+
ensure_rsi();
|
|
410
|
+
const double* rsi_vis =
|
|
411
|
+
rsi_cache.size() == candles.size()
|
|
412
|
+
? rsi_cache.data() + range.start
|
|
413
|
+
: nullptr;
|
|
414
|
+
const double* rsi_ma_vis =
|
|
415
|
+
(rsi.ma_visible &&
|
|
416
|
+
rsi_ma_cache.size() == candles.size())
|
|
417
|
+
? rsi_ma_cache.data() + range.start
|
|
418
|
+
: nullptr;
|
|
419
|
+
vroom::rsi_pane::draw(
|
|
420
|
+
canvas, *this, lay, visible, n, rsi_vis, rsi_ma_vis,
|
|
421
|
+
window_ms, visible_start_ms, candle_duration_ms,
|
|
422
|
+
candle_right, pane_top, pane_bottom);
|
|
423
|
+
} else {
|
|
424
|
+
ensure_macd();
|
|
425
|
+
const double* macd_vis =
|
|
426
|
+
macd_cache.size() == candles.size()
|
|
427
|
+
? macd_cache.data() + range.start
|
|
428
|
+
: nullptr;
|
|
429
|
+
const double* sig_vis =
|
|
430
|
+
macd_signal_cache.size() == candles.size()
|
|
431
|
+
? macd_signal_cache.data() + range.start
|
|
432
|
+
: nullptr;
|
|
433
|
+
const double* hist_vis =
|
|
434
|
+
macd_hist_cache.size() == candles.size()
|
|
435
|
+
? macd_hist_cache.data() + range.start
|
|
436
|
+
: nullptr;
|
|
437
|
+
vroom::macd_pane::draw(
|
|
438
|
+
canvas, *this, lay, visible, n, macd_vis, sig_vis,
|
|
439
|
+
hist_vis, window_ms, visible_start_ms,
|
|
440
|
+
candle_duration_ms, candle_right, pane_top,
|
|
441
|
+
pane_bottom);
|
|
442
|
+
}
|
|
443
|
+
pane_top = pane_bottom;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (wrap) canvas->restore();
|
|
335
449
|
}
|
|
336
450
|
|
|
337
451
|
// 6. Axis backgrounds (mask any candle overflow). The x-axis separator
|
|
@@ -354,20 +468,25 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
354
468
|
|
|
355
469
|
// 7.5. Current-price line + box — above labels so the box covers any label
|
|
356
470
|
// it overlaps; tracks the latest close as the price scale moves.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
471
|
+
// Hidden during a fade's outgoing half: the close is already the new
|
|
472
|
+
// series and there is no snapshot to fade.
|
|
473
|
+
if (!fade_out) {
|
|
474
|
+
vroom::price_indicator::draw(canvas, *this, lay, bounds,
|
|
475
|
+
candle_right, candle_area_h);
|
|
476
|
+
|
|
477
|
+
// 7.55. Consumer-supplied price status lines — same tier as the
|
|
478
|
+
// current-price indicator (their badges must cover the labels
|
|
479
|
+
// underneath), but after it so a resting order at the last close
|
|
480
|
+
// stays readable.
|
|
481
|
+
vroom::price_lines::draw(canvas, *this, lay, bounds, candle_right,
|
|
482
|
+
candle_area_h);
|
|
483
|
+
}
|
|
365
484
|
|
|
366
485
|
// 7.6. Indicator panes stacked below the candles, ordered by enable
|
|
367
486
|
// sequence (most recently enabled at the bottom). Each pane is
|
|
368
487
|
// INDICATOR_HEIGHT_FRAC of the height; the candle pane already shrank
|
|
369
|
-
// to fit them (see layout()).
|
|
370
|
-
if (lay.indicator_area_h > 0.f) {
|
|
488
|
+
// to fit them (see layout()). A fade already drew them with the scene.
|
|
489
|
+
if (!fade_swap && lay.indicator_area_h > 0.f) {
|
|
371
490
|
struct ActivePane { int order; int type; }; // type: 0 = RSI, 1 = MACD
|
|
372
491
|
ActivePane panes[2];
|
|
373
492
|
int count = 0;
|
package/cpp/_core_src/chart.h
CHANGED
|
@@ -73,11 +73,14 @@ struct VroomChart {
|
|
|
73
73
|
|
|
74
74
|
// Interval morph: the outgoing candle geometry captured when a timeframe
|
|
75
75
|
// switch begins, indexed from the right of the visible slice (slot 0 =
|
|
76
|
-
// newest)
|
|
77
|
-
//
|
|
78
|
-
//
|
|
76
|
+
// newest). `interval_morph_fade` is the host's choice at capture time:
|
|
77
|
+
// false = slot-lerp each column into its counterpart; true = fade the
|
|
78
|
+
// snapshot out then the new scene in (see labels::interval_phase).
|
|
79
|
+
// Stored as normalized fractions so it survives the new bounds and a
|
|
80
|
+
// resize. Empty when not morphing.
|
|
79
81
|
std::vector<vroom::CandleSnapshot> morph_from;
|
|
80
82
|
float interval_morph_t = 1.f; // 1 = not morphing
|
|
83
|
+
bool interval_morph_fade = false;
|
|
81
84
|
// The pre-switch price scale and time window. The candle capture above is
|
|
82
85
|
// normalized against these, and the axes keep rendering their old ticks from
|
|
83
86
|
// them while fading out (see labels::interval_phase), so a label is never
|
|
@@ -299,6 +299,7 @@ extern "C" void vroom_chart_reset_view(VroomChart* chart) {
|
|
|
299
299
|
// here — drop any capture rather than leaving it to reshape the wrong data.
|
|
300
300
|
chart->morph_from.clear();
|
|
301
301
|
chart->interval_morph_t = 1.f;
|
|
302
|
+
chart->interval_morph_fade = false;
|
|
302
303
|
apply_default_framing(chart);
|
|
303
304
|
vroom::labels::recompute_axis_width(*chart);
|
|
304
305
|
if (chart->cb.on_viewport_changed) {
|
|
@@ -352,10 +353,11 @@ extern "C" void vroom_chart_preserve_price_envelope(VroomChart* chart,
|
|
|
352
353
|
chart->mark_dirty();
|
|
353
354
|
}
|
|
354
355
|
|
|
355
|
-
extern "C" void vroom_chart_begin_interval_morph(VroomChart* chart) {
|
|
356
|
+
extern "C" void vroom_chart_begin_interval_morph(VroomChart* chart, int32_t mode) {
|
|
356
357
|
if (!chart) return;
|
|
357
358
|
chart->morph_from.clear();
|
|
358
359
|
chart->interval_morph_t = 1.f;
|
|
360
|
+
chart->interval_morph_fade = false;
|
|
359
361
|
|
|
360
362
|
const auto lay = chart->layout();
|
|
361
363
|
const float area_w = vroom::candle_area_width(lay);
|
|
@@ -400,6 +402,7 @@ extern "C" void vroom_chart_begin_interval_morph(VroomChart* chart) {
|
|
|
400
402
|
// Open the morph at 0 rather than leaving it at 1: the caller still has to
|
|
401
403
|
// push the new candles, and any frame painted in between should show the
|
|
402
404
|
// captured geometry — which is what the pre-switch frame looked like.
|
|
405
|
+
chart->interval_morph_fade = vroom::interval_morph_is_fade(mode);
|
|
403
406
|
chart->interval_morph_t = 0.f;
|
|
404
407
|
}
|
|
405
408
|
|
|
@@ -409,6 +412,7 @@ extern "C" void vroom_chart_set_interval_morph(VroomChart* chart, float t) {
|
|
|
409
412
|
if (chart->interval_morph_t >= 1.f) {
|
|
410
413
|
chart->morph_from.clear();
|
|
411
414
|
chart->morph_from.shrink_to_fit();
|
|
415
|
+
chart->interval_morph_fade = false;
|
|
412
416
|
}
|
|
413
417
|
chart->mark_dirty();
|
|
414
418
|
}
|
package/cpp/_core_src/viewport.h
CHANGED
|
@@ -104,6 +104,10 @@ inline std::size_t morph_from_count(const CandleSnapshot* from,
|
|
|
104
104
|
return (from && morph_t < 1.f) ? from_n : 0;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// C facade `vroom_chart_begin_interval_morph` mode: 0 = slot-lerp transform,
|
|
108
|
+
// anything else = fade the outgoing snapshot out then the new scene in.
|
|
109
|
+
inline bool interval_morph_is_fade(int32_t mode) { return mode != 0; }
|
|
110
|
+
|
|
107
111
|
// Returns the indices of candles whose time_ms falls in [start_ms, end_ms].
|
|
108
112
|
// When both are 0, returns the full range (Phase 1 default-everything behavior).
|
|
109
113
|
// Candles must be sorted ascending by time_ms (invariant of the public API).
|
package/lib/index.d.mts
CHANGED
|
@@ -187,6 +187,13 @@ type ChartType = 'candles' | 'line';
|
|
|
187
187
|
* Defaults to `'ease-in-out'`.
|
|
188
188
|
*/
|
|
189
189
|
type TransitionEasing = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
190
|
+
/**
|
|
191
|
+
* How a same-asset interval switch animates. `'transform'` (default) lerps
|
|
192
|
+
* each visible column into its counterpart. `'fade'` fades the old scene out
|
|
193
|
+
* then the new one in — use when the two windows don’t share a 1:1 pairing
|
|
194
|
+
* (e.g. a fixed lookback).
|
|
195
|
+
*/
|
|
196
|
+
type IntervalTransition = 'transform' | 'fade';
|
|
190
197
|
/** Active drawing tool while in `draw` mode. `null` draws nothing. */
|
|
191
198
|
type DrawTool = null | 'line' | 'box' | 'pencil' | 'path';
|
|
192
199
|
/** A drawing anchor in data space, so it stays glued to the candles on pan/zoom. */
|
|
@@ -200,9 +207,12 @@ type DrawPoint = {
|
|
|
200
207
|
type DrawingBase = {
|
|
201
208
|
/** Stable unique id (the chart generates one for drawings it creates). */
|
|
202
209
|
id: string;
|
|
203
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* Stroke color (hex string or packed ARGB number). New drawings take
|
|
212
|
+
* `drawingStyle.color` when set; otherwise `#ff2962ff`.
|
|
213
|
+
*/
|
|
204
214
|
color?: VroomColor;
|
|
205
|
-
/** Stroke width in px.
|
|
215
|
+
/** Stroke width in px. New drawings take `drawingStyle.width` when set; otherwise 2. */
|
|
206
216
|
width?: number;
|
|
207
217
|
/**
|
|
208
218
|
* Protect the drawing from editing. A locked drawing still renders and can
|
|
@@ -295,6 +305,29 @@ type DrawingStyle = {
|
|
|
295
305
|
fill?: VroomColor;
|
|
296
306
|
locked?: boolean;
|
|
297
307
|
};
|
|
308
|
+
/**
|
|
309
|
+
* Default appearance for drawings the user creates — the live draft and the
|
|
310
|
+
* object handed to `onDrawingComplete`. Paste copies the source drawing's
|
|
311
|
+
* style instead of this default.
|
|
312
|
+
*
|
|
313
|
+
* Omitted fields keep the library defaults: a 2px `#ff2962ff` stroke, and for
|
|
314
|
+
* boxes a 10% tint of the stroke as fill.
|
|
315
|
+
*
|
|
316
|
+
* Prefer 6-digit hex for `color` (`'#00FFFF'`). vroom treats that as opaque,
|
|
317
|
+
* and CSS swatches preview it correctly. 8-digit hex is `#aarrggbb`, not CSS
|
|
318
|
+
* `#rrggbbaa`.
|
|
319
|
+
*/
|
|
320
|
+
type DefaultDrawingStyle = {
|
|
321
|
+
/** Stroke color, used for the live draft and stamped onto the committed drawing. */
|
|
322
|
+
color?: VroomColor;
|
|
323
|
+
/** Stroke width in px. Default 2. */
|
|
324
|
+
width?: number;
|
|
325
|
+
/**
|
|
326
|
+
* Box interior fill. Omitted, new boxes keep the default 10% stroke tint.
|
|
327
|
+
* Ignored by line, pencil, and path.
|
|
328
|
+
*/
|
|
329
|
+
fill?: VroomColor;
|
|
330
|
+
};
|
|
298
331
|
/**
|
|
299
332
|
* Storage adapter for **managed** drawing persistence. Provide it via the
|
|
300
333
|
* `drawingStore` prop and the chart owns the drawings array itself — loading and
|
|
@@ -720,16 +753,23 @@ type VroomChartCoreProps = {
|
|
|
720
753
|
chartType?: ChartType;
|
|
721
754
|
/**
|
|
722
755
|
* Duration (ms) of the animated transitions: the candle↔line switch when
|
|
723
|
-
* `chartType` changes, and the
|
|
756
|
+
* `chartType` changes, and the interval switch when the `candles` array is
|
|
724
757
|
* swapped for a different interval of the same asset. Default ~300. `0` snaps
|
|
725
|
-
* instantly. Ignored (snaps) when the OS requests reduced motion
|
|
726
|
-
* instead uses a plain cross-fade.
|
|
758
|
+
* instantly. Ignored (snaps) when the OS requests reduced motion.
|
|
727
759
|
*/
|
|
728
760
|
transitionMs?: number;
|
|
729
761
|
/**
|
|
730
762
|
* Easing curve applied to those transitions. Default `'ease-in-out'`.
|
|
731
763
|
*/
|
|
732
764
|
transitionEasing?: TransitionEasing;
|
|
765
|
+
/**
|
|
766
|
+
* How a same-asset interval switch animates. `'transform'` (default) lerps
|
|
767
|
+
* each visible column into its counterpart. `'fade'` fades the old scene out
|
|
768
|
+
* then the new one in — use when the two windows don’t share a 1:1 pairing
|
|
769
|
+
* (e.g. a fixed lookback). Same duration/easing as `transitionMs`. Reduced
|
|
770
|
+
* motion still snaps.
|
|
771
|
+
*/
|
|
772
|
+
intervalTransition?: IntervalTransition;
|
|
733
773
|
theme?: VroomTheme;
|
|
734
774
|
/** RSI indicator (pane below the candles). Omit/disable to hide it. */
|
|
735
775
|
rsi?: RSIConfig;
|
|
@@ -802,6 +842,12 @@ type VroomChartCoreProps = {
|
|
|
802
842
|
mode?: ChartMode;
|
|
803
843
|
/** Active drawing tool while in `draw` mode. Default `null` (draws nothing). */
|
|
804
844
|
tool?: DrawTool;
|
|
845
|
+
/**
|
|
846
|
+
* Default appearance for drawings the user creates (live draft + the object
|
|
847
|
+
* handed to `onDrawingComplete`). Paste copies the source drawing's style
|
|
848
|
+
* instead of this. Omitted fields keep the library defaults.
|
|
849
|
+
*/
|
|
850
|
+
drawingStyle?: DefaultDrawingStyle;
|
|
805
851
|
/**
|
|
806
852
|
* Committed drawings to render, anchored to data so they track the candles on
|
|
807
853
|
* pan/zoom. This is a controlled prop: append the value the chart hands you in
|
|
@@ -945,4 +991,4 @@ declare function classifyTransition(prev: Candle[] | null, next: Candle[], serie
|
|
|
945
991
|
*/
|
|
946
992
|
declare function timeframeWindow(oldWindow: VisibleRange, oldStepMs: number, oldLastMs: number, newStepMs: number, newLastMs: number): VisibleRange;
|
|
947
993
|
|
|
948
|
-
export { type BollingerBandsConfig, type Candle, type ChartType, type CrosshairEvent, type DataTransition, type MACDConfig, type MAKind, type MASource, type MovingAverageOverlay, type PriceLine, type PriceLinesStyle, type RSIConfig, type TransitionEasing, type VWAPConfig, type VisibleRange, type VolumeConfig, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme, classifyTransition, inferStepMs, timeframeWindow };
|
|
994
|
+
export { type BollingerBandsConfig, type Candle, type ChartType, type CrosshairEvent, type DataTransition, type DefaultDrawingStyle, type IntervalTransition, type MACDConfig, type MAKind, type MASource, type MovingAverageOverlay, type PriceLine, type PriceLinesStyle, type RSIConfig, type TransitionEasing, type VWAPConfig, type VisibleRange, type VolumeConfig, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme, classifyTransition, inferStepMs, timeframeWindow };
|