react-native-vroom-chart 0.5.0 → 0.7.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 +143 -0
- package/android/README.md +60 -5
- package/android/build.gradle +115 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/VroomChartJni.cpp +24 -0
- package/android/src/main/java/com/vroom/chart/VroomChartModule.kt +31 -0
- package/android/src/main/java/com/vroom/chart/VroomChartPackage.kt +31 -0
- package/cpp/VroomChartHostObject.cpp +368 -33
- package/cpp/VroomJsiInstaller.cpp +11 -1
- package/cpp/VroomSkiaContext.cpp +15 -7
- package/cpp/_core_include/vroom/vroom_chart.h +250 -22
- 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 +98 -40
- package/cpp/_core_src/chart.h +82 -20
- package/cpp/_core_src/chart_facade.cpp +297 -66
- 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.cpp +87 -0
- package/cpp/_core_src/price_line_layout.h +80 -0
- package/cpp/_core_src/price_lines.cpp +428 -0
- package/cpp/_core_src/price_lines.h +56 -0
- 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 +261 -25
- package/lib/index.d.ts +261 -25
- package/lib/index.js +252 -36
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +252 -36
- package/lib/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/VroomChart.tsx +162 -11
- package/src/easing.ts +40 -0
- package/src/index.ts +5 -0
- package/src/jsi.d.ts +124 -20
- package/src/theme.ts +1 -0
- package/src/types.ts +5 -0
- package/src/useChartCore.ts +166 -28
|
@@ -51,7 +51,7 @@ typedef struct VroomOverlay {
|
|
|
51
51
|
|
|
52
52
|
// Bollinger Bands overlay drawn on the price pane: a basis MA of `source` over
|
|
53
53
|
// `period`, banded at ± `mult` × population standard deviation of the same
|
|
54
|
-
// window. Per
|
|
54
|
+
// window. Per the standard semantics the stdev always uses the window's
|
|
55
55
|
// arithmetic mean, even when `basis_kind` selects an EMA basis line.
|
|
56
56
|
typedef struct VroomBollinger {
|
|
57
57
|
int32_t enabled; // 0/1
|
|
@@ -69,6 +69,97 @@ typedef struct VroomBollinger {
|
|
|
69
69
|
float fill_opacity; // 0..1, multiplied into upper_color's alpha
|
|
70
70
|
} VroomBollinger;
|
|
71
71
|
|
|
72
|
+
// MACD, drawn in its own pane below the candles: the difference between a fast
|
|
73
|
+
// and a slow moving average of `source`, a signal line smoothing that
|
|
74
|
+
// difference, and a histogram of the gap between the two.
|
|
75
|
+
//
|
|
76
|
+
// The style fields carry an inherit sentinel so an untouched config renders the
|
|
77
|
+
// stock look: a fully transparent color falls back to the theme (histogram) or
|
|
78
|
+
// to the built-in default (lines), and a non-positive width falls back to 1.5.
|
|
79
|
+
// The two `fading` histogram colors — used when a bar moves back toward zero,
|
|
80
|
+
// i.e. momentum is easing — fall back to their base color at half alpha.
|
|
81
|
+
typedef struct VroomMACD {
|
|
82
|
+
int32_t enabled; // 0/1
|
|
83
|
+
int32_t fast; // fast MA length (clamped >= 1; default 12)
|
|
84
|
+
int32_t slow; // slow MA length (forced > fast; default 26)
|
|
85
|
+
int32_t signal; // signal MA length (clamped >= 1; default 9)
|
|
86
|
+
int32_t source; // 0=close,1=open,2=high,3=low,4=hl2,5=hlc3,6=ohlc4
|
|
87
|
+
int32_t ma_kind; // fast/slow legs: 0 = SMA, 1 = EMA
|
|
88
|
+
int32_t signal_ma_kind; // signal line: 0 = SMA, 1 = EMA
|
|
89
|
+
|
|
90
|
+
uint32_t line_color; // MACD line; 0xAARRGGBB, 0 inherits the default blue
|
|
91
|
+
float line_width; // stroke px; <= 0 inherits 1.5
|
|
92
|
+
int32_t line_visible; // 0/1
|
|
93
|
+
uint32_t signal_color; // 0 inherits the default orange
|
|
94
|
+
float signal_width;
|
|
95
|
+
int32_t signal_visible;
|
|
96
|
+
|
|
97
|
+
int32_t hist_visible;
|
|
98
|
+
uint32_t hist_up_color; // 0 inherits VROOM_COLOR_ACCENT_BULL
|
|
99
|
+
uint32_t hist_up_fading_color; // 0 inherits hist_up_color at half alpha
|
|
100
|
+
uint32_t hist_down_color; // 0 inherits VROOM_COLOR_ACCENT_BEAR
|
|
101
|
+
uint32_t hist_down_fading_color; // 0 inherits hist_down_color at half alpha
|
|
102
|
+
|
|
103
|
+
uint32_t zero_color; // 0 inherits the default gray
|
|
104
|
+
int32_t zero_visible; // 0/1
|
|
105
|
+
} VroomMACD;
|
|
106
|
+
|
|
107
|
+
// RSI, drawn in its own pane below the candles: the index line, an optional
|
|
108
|
+
// moving-average trendline over it, and two dashed rules at the overbought and
|
|
109
|
+
// oversold levels. Close-only by design (Wilder's definition), so unlike the
|
|
110
|
+
// other averaged indicators it takes no `source`.
|
|
111
|
+
//
|
|
112
|
+
// The style fields carry the same inherit sentinel as VroomMACD: a fully
|
|
113
|
+
// transparent color falls back to the built-in default and a non-positive width
|
|
114
|
+
// falls back to 1.5.
|
|
115
|
+
typedef struct VroomRSI {
|
|
116
|
+
int32_t enabled; // 0/1
|
|
117
|
+
int32_t period; // lookback in candles (clamped >= 2; default 14)
|
|
118
|
+
double upper_band; // overbought level (0..100; default 70)
|
|
119
|
+
double lower_band; // oversold level (0..100; default 30)
|
|
120
|
+
int32_t ma_period; // trendline length (clamped >= 1; default 14)
|
|
121
|
+
int32_t ma_kind; // trendline: 0 = SMA, 1 = EMA
|
|
122
|
+
int32_t ma_visible; // 0/1
|
|
123
|
+
|
|
124
|
+
uint32_t line_color; // 0 inherits the default violet
|
|
125
|
+
float line_width; // stroke px; <= 0 inherits 1.5
|
|
126
|
+
int32_t line_visible; // 0/1
|
|
127
|
+
uint32_t ma_color; // 0 inherits the default amber
|
|
128
|
+
float ma_width;
|
|
129
|
+
uint32_t band_color; // both dashed rules; 0 inherits the default gray
|
|
130
|
+
int32_t bands_visible; // 0/1
|
|
131
|
+
} VroomRSI;
|
|
132
|
+
|
|
133
|
+
// Session VWAP, drawn as a single line on the price pane. The session resets
|
|
134
|
+
// each UTC day shifted by `reset_offset_min` minutes, and the line lifts its pen
|
|
135
|
+
// at each reset. Color 0 inherits the default cyan; a non-positive width
|
|
136
|
+
// inherits 1.5.
|
|
137
|
+
typedef struct VroomVWAP {
|
|
138
|
+
int32_t enabled; // 0/1
|
|
139
|
+
int32_t reset_offset_min; // session boundary offset from UTC midnight
|
|
140
|
+
uint32_t color; // 0xAARRGGBB; 0 inherits the default
|
|
141
|
+
float width; // stroke px; <= 0 inherits 1.5
|
|
142
|
+
} VroomVWAP;
|
|
143
|
+
|
|
144
|
+
// Volume bars on the price pane, one per candle, drawn under the candles.
|
|
145
|
+
//
|
|
146
|
+
// `height_frac` is a ceiling, not a reservation: raising it lets the tallest bar
|
|
147
|
+
// reach further up over the candles rather than compressing them (matching the
|
|
148
|
+
// conventional volume overlay). Bar heights always auto-fit the loudest volume
|
|
149
|
+
// in view, so the tallest bar sits exactly at the ceiling.
|
|
150
|
+
//
|
|
151
|
+
// The style fields carry an inherit sentinel so the theme keeps supplying them
|
|
152
|
+
// when the consumer doesn't: a negative float or a fully transparent color falls
|
|
153
|
+
// back to the corresponding theme key.
|
|
154
|
+
typedef struct VroomVolume {
|
|
155
|
+
int32_t enabled; // 0/1 — default 1: bars draw unless turned off
|
|
156
|
+
float height_frac; // tallest bar as a fraction of the price pane; < 0 inherits 0.2
|
|
157
|
+
float opacity; // 0..1 (1 = opaque); < 0 inherits VROOM_FLOAT_VOLUME_OPACITY
|
|
158
|
+
float radius_px; // top-corner radius; < 0 inherits VROOM_FLOAT_VOLUME_RADIUS_PX
|
|
159
|
+
uint32_t up_color; // 0xAARRGGBB; 0 inherits VROOM_COLOR_ACCENT_BULL
|
|
160
|
+
uint32_t down_color; // 0xAARRGGBB; 0 inherits VROOM_COLOR_ACCENT_BEAR
|
|
161
|
+
} VroomVolume;
|
|
162
|
+
|
|
72
163
|
// A drawing anchor in data space (so a drawing tracks the candles on pan/zoom).
|
|
73
164
|
typedef struct VroomDrawPoint {
|
|
74
165
|
int64_t time_ms; // epoch milliseconds (not snapped to a candle slot)
|
|
@@ -118,6 +209,44 @@ typedef struct VroomLiquidityStyle {
|
|
|
118
209
|
float width_frac;
|
|
119
210
|
} VroomLiquidityStyle;
|
|
120
211
|
|
|
212
|
+
// ---- Price status lines ---------------------------------------------------
|
|
213
|
+
|
|
214
|
+
// Bit flags for VroomPriceLine::flags.
|
|
215
|
+
typedef enum {
|
|
216
|
+
VROOM_PRICE_LINE_DRAGGABLE = 1 << 0, // the line can be dragged vertically
|
|
217
|
+
VROOM_PRICE_LINE_CLOSABLE = 1 << 1, // render the trailing close ("x") button
|
|
218
|
+
VROOM_PRICE_LINE_AXIS_LABEL = 1 << 2, // render the price badge in the y-axis strip
|
|
219
|
+
VROOM_PRICE_LINE_EXTEND_LEFT = 1 << 3, // extend the line to the pane's left edge
|
|
220
|
+
} VroomPriceLineFlags;
|
|
221
|
+
|
|
222
|
+
// A consumer-supplied horizontal status line at a fixed price — the primitive
|
|
223
|
+
// behind resting limit orders, take-profits, liquidation levels and the like.
|
|
224
|
+
//
|
|
225
|
+
// Visually: a line across the price pane ending in a label group made of a body
|
|
226
|
+
// pill (`text`), an optional solid-filled `quantity` pill, and an optional close
|
|
227
|
+
// button, plus an optional price badge in the y-axis strip.
|
|
228
|
+
//
|
|
229
|
+
// `text` / `quantity` are UTF-8 and copied internally, so the caller may free
|
|
230
|
+
// them as soon as the call returns. A null or empty string hides that segment.
|
|
231
|
+
typedef struct VroomPriceLine {
|
|
232
|
+
double price;
|
|
233
|
+
uint32_t color; // 0xAARRGGBB — line, border, body text, close icon
|
|
234
|
+
float width; // stroke width in px
|
|
235
|
+
int32_t line_style; // 0 = solid, 1 = dotted, 2 = dashed
|
|
236
|
+
const char* text; // body label
|
|
237
|
+
const char* quantity; // trailing solid-fill segment
|
|
238
|
+
int32_t flags; // bitwise-or of VroomPriceLineFlags
|
|
239
|
+
} VroomPriceLine;
|
|
240
|
+
|
|
241
|
+
// Layout/style shared by every price line, so the per-line struct stays small.
|
|
242
|
+
typedef struct VroomPriceLineStyle {
|
|
243
|
+
uint32_t body_bg; // translucent body/close-button pill fill (0xAARRGGBB)
|
|
244
|
+
float font_size_px; // 0 = inherit VROOM_FLOAT_AXIS_FONT_SIZE_PX
|
|
245
|
+
float line_length_frac; // 0..1 of pane width: the label group's right-edge inset
|
|
246
|
+
int32_t align; // 0 = left, 1 = center, 2 = right
|
|
247
|
+
float hover_boost; // brightness multiplier for the hovered segment (1 = flat)
|
|
248
|
+
} VroomPriceLineStyle;
|
|
249
|
+
|
|
121
250
|
// A continuous data coordinate at a pixel position (no candle snapping). Used to
|
|
122
251
|
// translate a drawing-tool click into a data-space anchor.
|
|
123
252
|
typedef struct VroomCoord {
|
|
@@ -161,9 +290,25 @@ typedef enum {
|
|
|
161
290
|
VROOM_FLOAT_WICK_ROUND_CAP, // 0/1: round the wick end caps
|
|
162
291
|
VROOM_FLOAT_VOLUME_RADIUS_PX, // volume bar top-corner radius px (0 = square)
|
|
163
292
|
VROOM_FLOAT_LINE_WIDTH_PX, // line-chart-mode polyline stroke width px
|
|
293
|
+
VROOM_FLOAT_LINE_GRADIENT_OPACITY, // fill under the line at its strongest (0 disables)
|
|
164
294
|
VROOM_FLOAT_COUNT_
|
|
165
295
|
} VroomFloatKey;
|
|
166
296
|
|
|
297
|
+
// ---- Animation ------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
// Easing curves for the animations the core paces itself. Mirrors the
|
|
300
|
+
// TransitionEasing union in @vroomchart/types, index for index.
|
|
301
|
+
//
|
|
302
|
+
// Most animations are eased by the host before it hands the core a progress
|
|
303
|
+
// value; this enum exists for the ones the core has to ease internally because
|
|
304
|
+
// each element runs on its own slice of the timeline.
|
|
305
|
+
typedef enum {
|
|
306
|
+
VROOM_EASING_LINEAR = 0,
|
|
307
|
+
VROOM_EASING_IN,
|
|
308
|
+
VROOM_EASING_OUT,
|
|
309
|
+
VROOM_EASING_IN_OUT, // smoothstep; the default for unknown values
|
|
310
|
+
} VroomEasing;
|
|
311
|
+
|
|
167
312
|
// ---- Callbacks ------------------------------------------------------------
|
|
168
313
|
|
|
169
314
|
typedef struct {
|
|
@@ -225,6 +370,45 @@ void vroom_chart_reset_view(VroomChart* chart);
|
|
|
225
370
|
// switch) so the price scale re-fits the newly visible candles.
|
|
226
371
|
void vroom_chart_reset_price_scale(VroomChart* chart);
|
|
227
372
|
|
|
373
|
+
// Reads the visible price *envelope* — the min low / max high across the
|
|
374
|
+
// currently visible candles. This is the extent the candles actually occupy, not
|
|
375
|
+
// the axis range (which is wider: see vroom_chart_preserve_price_envelope).
|
|
376
|
+
// Returns false (out params untouched) when no candles are visible. Either out
|
|
377
|
+
// pointer may be null.
|
|
378
|
+
bool vroom_chart_get_visible_price_envelope(VroomChart* chart,
|
|
379
|
+
double* out_low, double* out_high);
|
|
380
|
+
|
|
381
|
+
// "Scale lock" for a same-asset data swap that re-buckets the same price action
|
|
382
|
+
// into a different high-low span (a timeframe switch). Rescales a *manual* price
|
|
383
|
+
// range so the visible envelope keeps the exact pixel height and position that
|
|
384
|
+
// the [prev_low, prev_high] envelope had before the swap — so candles don't
|
|
385
|
+
// suddenly shrink or grow when the interval changes.
|
|
386
|
+
//
|
|
387
|
+
// Call after set_candles + set_visible_range, passing the envelope read by
|
|
388
|
+
// vroom_chart_get_visible_price_envelope *before* the swap.
|
|
389
|
+
//
|
|
390
|
+
// No-op in auto mode: auto-fit already widens the envelope by a fixed factor, so
|
|
391
|
+
// its pixel height is invariant. Falls back to reset_price_scale semantics when
|
|
392
|
+
// either envelope is degenerate.
|
|
393
|
+
void vroom_chart_preserve_price_envelope(VroomChart* chart,
|
|
394
|
+
double prev_low, double prev_high);
|
|
395
|
+
|
|
396
|
+
// Captures the currently visible candle geometry so the next data swap can be
|
|
397
|
+
// animated as a reshape rather than a jump: each candle's wick and body slide
|
|
398
|
+
// and stretch into the shape of its counterpart in the new data.
|
|
399
|
+
//
|
|
400
|
+
// Candles are paired by *slot* — position counting back from the right edge of
|
|
401
|
+
// the visible window, which a timeframe switch preserves. Call before
|
|
402
|
+
// set_candles, then drive vroom_chart_set_interval_morph from 0 to 1.
|
|
403
|
+
// No-op when nothing is visible.
|
|
404
|
+
void vroom_chart_begin_interval_morph(VroomChart* chart);
|
|
405
|
+
|
|
406
|
+
// Advances the interval morph started by vroom_chart_begin_interval_morph. `t`
|
|
407
|
+
// (clamped to 0..1) is the eased progress: 0 renders the captured geometry
|
|
408
|
+
// pixel-identically to the pre-swap frame, 1 renders the new candles and
|
|
409
|
+
// releases the capture. Driven per-frame by the host animation loop.
|
|
410
|
+
void vroom_chart_set_interval_morph(VroomChart* chart, float t);
|
|
411
|
+
|
|
228
412
|
void vroom_chart_pan(VroomChart* chart, float dx_px, float dy_px);
|
|
229
413
|
// Directional zoom. scale_x scales the time window around focus_x_px (>1 =
|
|
230
414
|
// narrower window, wider candles); scale_y scales the price range around
|
|
@@ -313,39 +497,51 @@ bool vroom_chart_get_crosshair_info(VroomChart* chart, VroomCrosshairInfo* out);
|
|
|
313
497
|
|
|
314
498
|
// ---- Indicators -----------------------------------------------------------
|
|
315
499
|
|
|
316
|
-
// Configures the RSI indicator (rendered in a pane below the candles).
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
// are the EMA lengths (clamped >= 1, slow forced > fast; default 12/26) and
|
|
328
|
-
// `signal` is the signal-line EMA length (clamped >= 1; default 9). Panes stack
|
|
329
|
-
// in enable order, most recently enabled at the bottom.
|
|
330
|
-
void vroom_chart_set_macd(VroomChart* chart, bool enabled, int fast, int slow,
|
|
331
|
-
int signal);
|
|
500
|
+
// Configures the RSI indicator (rendered in a pane below the candles). When
|
|
501
|
+
// enabled, the candle pane shrinks by VROOM_FLOAT_INDICATOR_HEIGHT_FRAC. Length
|
|
502
|
+
// and MA-kind changes recompute the series; band levels, colors, widths, and
|
|
503
|
+
// visibility changes only re-render.
|
|
504
|
+
void vroom_chart_set_rsi(VroomChart* chart, const VroomRSI* cfg);
|
|
505
|
+
|
|
506
|
+
// Configures the MACD indicator (its own pane below the candles). Panes stack
|
|
507
|
+
// in enable order, most recently enabled at the bottom. Length, source, and MA
|
|
508
|
+
// kind changes recompute the series; color, width, and visibility changes only
|
|
509
|
+
// re-render.
|
|
510
|
+
void vroom_chart_set_macd(VroomChart* chart, const VroomMACD* cfg);
|
|
332
511
|
|
|
333
512
|
// Replaces the full set of moving-average overlay lines (SMA/EMA) drawn on the
|
|
334
513
|
// price pane. Pass count 0 to clear them. Overlays don't reserve a pane.
|
|
335
514
|
void vroom_chart_set_overlays(VroomChart* chart, const VroomOverlay* overlays,
|
|
336
515
|
size_t count);
|
|
337
516
|
|
|
338
|
-
// Configures the session VWAP overlay (a single price-pane line).
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
void vroom_chart_set_vwap(VroomChart* chart,
|
|
342
|
-
uint32_t color, float width);
|
|
517
|
+
// Configures the session VWAP overlay (a single price-pane line). Enabled and
|
|
518
|
+
// reset-time changes recompute the series; color and width changes only
|
|
519
|
+
// re-render.
|
|
520
|
+
void vroom_chart_set_vwap(VroomChart* chart, const VroomVWAP* cfg);
|
|
343
521
|
|
|
344
522
|
// Configures the Bollinger Bands overlay (three price-pane lines + an optional
|
|
345
523
|
// translucent fill between the bands; no pane is reserved). Color/width/fill
|
|
346
524
|
// changes only re-render; enabled/period/mult/source/basis changes recompute.
|
|
347
525
|
void vroom_chart_set_bollinger(VroomChart* chart, const VroomBollinger* cfg);
|
|
348
526
|
|
|
527
|
+
// Configures the volume bars. Render-only — the bars come straight off each
|
|
528
|
+
// candle's volume, so nothing is recomputed. Bars are enabled by default; pass
|
|
529
|
+
// a config with `enabled` 0 to hide them.
|
|
530
|
+
void vroom_chart_set_volume(VroomChart* chart, const VroomVolume* cfg);
|
|
531
|
+
|
|
532
|
+
// Advances the staggered volume-bar collapse: 0 leaves every bar at full height,
|
|
533
|
+
// 1 has them all flat. Driven per-frame by the host animation loop.
|
|
534
|
+
//
|
|
535
|
+
// `t` is *linear* progress, unlike the other morph setters. Each bar falls over
|
|
536
|
+
// its own slice of the timeline — the tallest starting first and every bar
|
|
537
|
+
// landing at 1 — so `easing` (a VroomEasing) is applied per bar inside the core;
|
|
538
|
+
// pre-easing here would compound the two curves.
|
|
539
|
+
//
|
|
540
|
+
// Symmetric, so driving 1 -> 0 reveals the bars: the shortest rises first and
|
|
541
|
+
// the tallest arrives last. vroom_chart_set_volume snaps this to match its
|
|
542
|
+
// `enabled`, so the host only needs it while animating.
|
|
543
|
+
void vroom_chart_set_volume_collapse(VroomChart* chart, float t, int32_t easing);
|
|
544
|
+
|
|
349
545
|
// ---- Drawings (line annotations) ------------------------------------------
|
|
350
546
|
|
|
351
547
|
// Replaces the full set of committed line drawings (data-anchored, so they track
|
|
@@ -390,6 +586,38 @@ void vroom_chart_translate_drawing(VroomChart* chart, int32_t index,
|
|
|
390
586
|
void vroom_chart_set_liquidity(VroomChart* chart, const VroomBand* bands,
|
|
391
587
|
size_t count, const VroomLiquidityStyle* style);
|
|
392
588
|
|
|
589
|
+
// ---- Price status lines ---------------------------------------------------
|
|
590
|
+
|
|
591
|
+
// Replaces the full set of price status lines and their shared style. Lines
|
|
592
|
+
// render on the price pane above the axis labels (so their badges cover any
|
|
593
|
+
// label they overlap) and below the crosshair. A line whose price maps outside
|
|
594
|
+
// the price pane is skipped rather than clamped. Pass count 0 to clear;
|
|
595
|
+
// `style` may be null when count is 0.
|
|
596
|
+
void vroom_chart_set_price_lines(VroomChart* chart, const VroomPriceLine* lines,
|
|
597
|
+
size_t count,
|
|
598
|
+
const VroomPriceLineStyle* style);
|
|
599
|
+
|
|
600
|
+
// Hit-tests pixel (x_px, y_px) against the price lines. On a hit, fills
|
|
601
|
+
// *out_index with the line index and *out_part with 0 (the line or its label
|
|
602
|
+
// body — the drag target) or 1 (the close button), and returns true. When
|
|
603
|
+
// several lines are within tolerance the nearest in y wins. Only draggable
|
|
604
|
+
// lines report part 0 and only closable lines report part 1. Returns false on a
|
|
605
|
+
// miss (out params untouched). Either out pointer may be null.
|
|
606
|
+
bool vroom_chart_hit_test_price_line(VroomChart* chart, float x_px, float y_px,
|
|
607
|
+
int32_t* out_index, int32_t* out_part);
|
|
608
|
+
|
|
609
|
+
// Marks a price line's segment as hovered so it renders highlighted. `index` -1
|
|
610
|
+
// clears the hover. `part` matches vroom_chart_hit_test_price_line.
|
|
611
|
+
void vroom_chart_set_price_line_hover(VroomChart* chart, int32_t index,
|
|
612
|
+
int32_t part);
|
|
613
|
+
|
|
614
|
+
// Drives the live drag preview: the line, its label and its badge render at
|
|
615
|
+
// `price` instead of the committed one, and a faint ghost marks where it
|
|
616
|
+
// started. `index` -1 ends the preview. The committed price is never mutated —
|
|
617
|
+
// the host applies (or rejects) the new price by restating its lines.
|
|
618
|
+
void vroom_chart_set_price_line_drag(VroomChart* chart, int32_t index,
|
|
619
|
+
double price);
|
|
620
|
+
|
|
393
621
|
// Sets the transient in-progress "draft" the drawing tool shows while the user
|
|
394
622
|
// places points. Node A is always shown; when `has_b`, node B is shown too.
|
|
395
623
|
// `guide != 0` also draws the live preview (a guideline for a line, or a preview
|
|
@@ -19,7 +19,7 @@ namespace vroom::bollinger {
|
|
|
19
19
|
// period window
|
|
20
20
|
//
|
|
21
21
|
// The stdev always uses the window's arithmetic mean, even when basis_kind
|
|
22
|
-
// selects an EMA basis line (
|
|
22
|
+
// selects an EMA basis line (the standard semantics).
|
|
23
23
|
//
|
|
24
24
|
// Each output is resized to n; values are NaN for i < period-1 and when
|
|
25
25
|
// n < period.
|
|
@@ -20,6 +20,31 @@ namespace {
|
|
|
20
20
|
inline uint32_t resolve_color(uint32_t color, uint32_t fill) {
|
|
21
21
|
return (color >> 24) == 0 ? fill : color;
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
// Channel-wise ARGB blend, for a candle that changes direction mid-morph.
|
|
25
|
+
inline uint32_t lerp_argb(uint32_t a, uint32_t b, float t) {
|
|
26
|
+
uint32_t out = 0;
|
|
27
|
+
for (int shift = 0; shift < 32; shift += 8) {
|
|
28
|
+
const float ca = static_cast<float>((a >> shift) & 0xFFu);
|
|
29
|
+
const float cb = static_cast<float>((b >> shift) & 0xFFu);
|
|
30
|
+
const auto v = static_cast<uint32_t>(ca + (cb - ca) * t + 0.5f);
|
|
31
|
+
out |= v << shift;
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
inline uint32_t scale_alpha(uint32_t argb, float a) {
|
|
37
|
+
const auto alpha =
|
|
38
|
+
static_cast<uint32_t>(static_cast<float>((argb >> 24) & 0xFFu) * a + 0.5f);
|
|
39
|
+
return (alpha << 24) | (argb & 0x00FFFFFFu);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
inline float lerp(float a, float b, float t) { return a + (b - a) * t; }
|
|
43
|
+
|
|
44
|
+
// One slot's wick/body extents in pixels.
|
|
45
|
+
struct SlotGeom {
|
|
46
|
+
float x, open, high, low, close;
|
|
47
|
+
};
|
|
23
48
|
} // namespace
|
|
24
49
|
|
|
25
50
|
void draw(SkCanvas* canvas,
|
|
@@ -32,13 +57,23 @@ void draw(SkCanvas* canvas,
|
|
|
32
57
|
int64_t visible_start_ms,
|
|
33
58
|
int64_t candle_duration_ms,
|
|
34
59
|
float collapse,
|
|
35
|
-
float opacity
|
|
36
|
-
|
|
60
|
+
float opacity,
|
|
61
|
+
const CandleSnapshot* from,
|
|
62
|
+
std::size_t from_n,
|
|
63
|
+
float morph_t) {
|
|
64
|
+
if (!canvas) return;
|
|
37
65
|
|
|
38
66
|
collapse = std::clamp(collapse, 0.f, 1.f);
|
|
39
67
|
opacity = std::clamp(opacity, 0.f, 1.f);
|
|
68
|
+
morph_t = std::clamp(morph_t, 0.f, 1.f);
|
|
40
69
|
if (opacity <= 0.f) return;
|
|
41
70
|
|
|
71
|
+
// A finished morph drops the capture, reducing the loop below to the plain
|
|
72
|
+
// candle path.
|
|
73
|
+
const std::size_t from_count = vroom::morph_from_count(from, from_n, morph_t);
|
|
74
|
+
const std::size_t slots = std::max(n, from_count);
|
|
75
|
+
if (slots == 0) return;
|
|
76
|
+
|
|
42
77
|
// During the candle→line morph, fade the entire candle layer as one unit so
|
|
43
78
|
// overlapping bars/wicks composite cleanly instead of stacking alpha.
|
|
44
79
|
const bool fade_layer = opacity < 0.999f;
|
|
@@ -60,13 +95,20 @@ void draw(SkCanvas* canvas,
|
|
|
60
95
|
const bool wick_round = theme.floats[VROOM_FLOAT_WICK_ROUND_CAP] > 0.5f;
|
|
61
96
|
|
|
62
97
|
constexpr float kBorderWidthPx = 1.f;
|
|
98
|
+
const uint32_t wick_color_bull =
|
|
99
|
+
resolve_color(theme.colors[VROOM_COLOR_WICK_BULL], fill_bull);
|
|
100
|
+
const uint32_t wick_color_bear =
|
|
101
|
+
resolve_color(theme.colors[VROOM_COLOR_WICK_BEAR], fill_bear);
|
|
102
|
+
const uint32_t border_color_bull =
|
|
103
|
+
resolve_color(theme.colors[VROOM_COLOR_BORDER_BULL], fill_bull);
|
|
104
|
+
const uint32_t border_color_bear =
|
|
105
|
+
resolve_color(theme.colors[VROOM_COLOR_BORDER_BEAR], fill_bear);
|
|
106
|
+
|
|
63
107
|
// Draw the border only when it differs from the fill; an inherited/transparent
|
|
64
108
|
// border color (the default, and the "hide" path) renders nothing — so a hidden
|
|
65
109
|
// border costs no pixels and never affects candle width.
|
|
66
|
-
const bool draw_border_bull =
|
|
67
|
-
|
|
68
|
-
const bool draw_border_bear =
|
|
69
|
-
resolve_color(theme.colors[VROOM_COLOR_BORDER_BEAR], fill_bear) != fill_bear;
|
|
110
|
+
const bool draw_border_bull = border_color_bull != fill_bull;
|
|
111
|
+
const bool draw_border_bear = border_color_bear != fill_bear;
|
|
70
112
|
|
|
71
113
|
SkPaint bull_paint;
|
|
72
114
|
bull_paint.setAntiAlias(true);
|
|
@@ -78,15 +120,13 @@ void draw(SkCanvas* canvas,
|
|
|
78
120
|
|
|
79
121
|
SkPaint wick_bull;
|
|
80
122
|
wick_bull.setAntiAlias(true);
|
|
81
|
-
wick_bull.setColor(
|
|
82
|
-
resolve_color(theme.colors[VROOM_COLOR_WICK_BULL], fill_bull));
|
|
123
|
+
wick_bull.setColor(wick_color_bull);
|
|
83
124
|
wick_bull.setStrokeWidth(theme.floats[VROOM_FLOAT_WICK_WIDTH_PX]);
|
|
84
125
|
wick_bull.setStyle(SkPaint::kStroke_Style);
|
|
85
126
|
wick_bull.setStrokeCap(wick_round ? SkPaint::kRound_Cap : SkPaint::kButt_Cap);
|
|
86
127
|
|
|
87
128
|
SkPaint wick_bear = wick_bull;
|
|
88
|
-
wick_bear.setColor(
|
|
89
|
-
resolve_color(theme.colors[VROOM_COLOR_WICK_BEAR], fill_bear));
|
|
129
|
+
wick_bear.setColor(wick_color_bear);
|
|
90
130
|
|
|
91
131
|
// 1px body outlines, drawn *inside* the body (see the inset at draw time) so
|
|
92
132
|
// a contrasting border never widens the candle. Default sentinel resolves to
|
|
@@ -95,52 +135,110 @@ void draw(SkCanvas* canvas,
|
|
|
95
135
|
border_bull.setAntiAlias(true);
|
|
96
136
|
border_bull.setStyle(SkPaint::kStroke_Style);
|
|
97
137
|
border_bull.setStrokeWidth(kBorderWidthPx);
|
|
98
|
-
border_bull.setColor(
|
|
99
|
-
resolve_color(theme.colors[VROOM_COLOR_BORDER_BULL], fill_bull));
|
|
138
|
+
border_bull.setColor(border_color_bull);
|
|
100
139
|
|
|
101
140
|
SkPaint border_bear = border_bull;
|
|
102
|
-
border_bear.setColor(
|
|
103
|
-
|
|
141
|
+
border_bear.setColor(border_color_bear);
|
|
142
|
+
|
|
143
|
+
// Scratch paints for the slots that need a per-slot color (see below).
|
|
144
|
+
SkPaint slot_wick, slot_fill, slot_border;
|
|
104
145
|
|
|
105
146
|
const float half_body = body_w * 0.5f;
|
|
147
|
+
const float area_w = vroom::candle_area_width(lay);
|
|
106
148
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
149
|
+
// Iterate slots counting back from the right edge — the pairing a timeframe
|
|
150
|
+
// switch preserves — descending so the paint order stays oldest-first and
|
|
151
|
+
// touching wicks/borders composite exactly as they do without a morph.
|
|
152
|
+
for (std::size_t k = slots; k-- > 0;) {
|
|
153
|
+
const ::VroomCandle* to = (k < n) ? &visible[n - 1 - k] : nullptr;
|
|
154
|
+
const CandleSnapshot* frm = (k < from_count) ? &from[k] : nullptr;
|
|
155
|
+
|
|
156
|
+
SlotGeom g{};
|
|
157
|
+
bool bull;
|
|
158
|
+
float alpha = 1.f;
|
|
159
|
+
|
|
160
|
+
if (to) {
|
|
161
|
+
const SlotGeom t{
|
|
162
|
+
vroom::candle_center_x(lay, to->time_ms, candle_duration_ms,
|
|
163
|
+
visible_start_ms, window_ms),
|
|
164
|
+
vroom::price_to_y(lay, bounds, to->open),
|
|
165
|
+
vroom::price_to_y(lay, bounds, to->high),
|
|
166
|
+
vroom::price_to_y(lay, bounds, to->low),
|
|
167
|
+
vroom::price_to_y(lay, bounds, to->close),
|
|
168
|
+
};
|
|
169
|
+
bull = to->close >= to->open;
|
|
170
|
+
if (frm) {
|
|
171
|
+
// The snapshot is in band fractions, so it lands on the same
|
|
172
|
+
// pixels it occupied pre-switch even though the bounds changed.
|
|
173
|
+
g.x = lerp(frm->x * area_w, t.x, morph_t);
|
|
174
|
+
g.open = lerp(vroom::y_at_fraction(lay, frm->open), t.open, morph_t);
|
|
175
|
+
g.high = lerp(vroom::y_at_fraction(lay, frm->high), t.high, morph_t);
|
|
176
|
+
g.low = lerp(vroom::y_at_fraction(lay, frm->low), t.low, morph_t);
|
|
177
|
+
g.close =
|
|
178
|
+
lerp(vroom::y_at_fraction(lay, frm->close), t.close, morph_t);
|
|
179
|
+
} else {
|
|
180
|
+
g = t;
|
|
181
|
+
// A slot with no counterpart: fade in rather than pop.
|
|
182
|
+
if (from_count > 0) alpha = morph_t;
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
g = {frm->x * area_w,
|
|
186
|
+
vroom::y_at_fraction(lay, frm->open),
|
|
187
|
+
vroom::y_at_fraction(lay, frm->high),
|
|
188
|
+
vroom::y_at_fraction(lay, frm->low),
|
|
189
|
+
vroom::y_at_fraction(lay, frm->close)};
|
|
190
|
+
bull = frm->bull;
|
|
191
|
+
alpha = 1.f - morph_t; // the new data dropped this slot
|
|
192
|
+
}
|
|
193
|
+
if (alpha <= 0.f) continue;
|
|
110
194
|
|
|
111
|
-
const float cx = vroom::candle_center_x(
|
|
112
|
-
lay, c.time_ms, candle_duration_ms,
|
|
113
|
-
visible_start_ms, window_ms);
|
|
114
|
-
const float y_close = vroom::price_to_y(lay, bounds, c.close);
|
|
115
195
|
// Collapse high/low/open toward the close so the candle folds into its
|
|
116
196
|
// close point (the line vertex) as `collapse` → 1.
|
|
117
|
-
const float
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const float
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
197
|
+
const float y_close = g.close;
|
|
198
|
+
const float y_high = g.high * (1.f - collapse) + y_close * collapse;
|
|
199
|
+
const float y_low = g.low * (1.f - collapse) + y_close * collapse;
|
|
200
|
+
const float y_open = g.open * (1.f - collapse) + y_close * collapse;
|
|
201
|
+
|
|
202
|
+
// Fast path: the prebuilt bull/bear paints. A faded slot and a candle
|
|
203
|
+
// that changes direction mid-morph both need per-slot colors.
|
|
204
|
+
const SkPaint* wick_p = bull ? &wick_bull : &wick_bear;
|
|
205
|
+
const SkPaint* fill_p = bull ? &bull_paint : &bear_paint;
|
|
206
|
+
const SkPaint* border_p = bull ? &border_bull : &border_bear;
|
|
207
|
+
bool draw_border = bull ? draw_border_bull : draw_border_bear;
|
|
208
|
+
|
|
209
|
+
const bool flip = frm && to && frm->bull != bull;
|
|
210
|
+
if (flip || alpha < 0.999f) {
|
|
211
|
+
const auto blend = [&](uint32_t c_bull, uint32_t c_bear) {
|
|
212
|
+
uint32_t c = bull ? c_bull : c_bear;
|
|
213
|
+
if (flip) c = lerp_argb(bull ? c_bear : c_bull, c, morph_t);
|
|
214
|
+
return scale_alpha(c, alpha);
|
|
215
|
+
};
|
|
216
|
+
slot_wick = *wick_p;
|
|
217
|
+
slot_wick.setColor(blend(wick_color_bull, wick_color_bear));
|
|
218
|
+
slot_fill = *fill_p;
|
|
219
|
+
slot_fill.setColor(blend(fill_bull, fill_bear));
|
|
220
|
+
slot_border = *border_p;
|
|
221
|
+
slot_border.setColor(blend(border_color_bull, border_color_bear));
|
|
222
|
+
wick_p = &slot_wick;
|
|
223
|
+
fill_p = &slot_fill;
|
|
224
|
+
border_p = &slot_border;
|
|
225
|
+
if (flip) draw_border = draw_border_bull || draw_border_bear;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
canvas->drawLine(g.x, y_high, g.x, y_low, *wick_p);
|
|
129
229
|
|
|
130
230
|
const float y_top = std::min(y_open, y_close);
|
|
131
231
|
const float y_bot = std::max(y_open, y_close);
|
|
132
232
|
const float h = std::max(1.f, y_bot - y_top);
|
|
133
|
-
const SkRect body = SkRect::MakeXYWH(
|
|
233
|
+
const SkRect body = SkRect::MakeXYWH(g.x - half_body, y_top, body_w, h);
|
|
134
234
|
// Corner radius clamped so thin candles / short bodies don't over-round.
|
|
135
235
|
const float r = std::min({body_r, body_w * 0.5f, h * 0.5f});
|
|
136
|
-
const SkPaint& fill = bull ? bull_paint : bear_paint;
|
|
137
236
|
if (r > 0.f) {
|
|
138
|
-
canvas->drawRoundRect(body, r, r,
|
|
237
|
+
canvas->drawRoundRect(body, r, r, *fill_p);
|
|
139
238
|
} else {
|
|
140
|
-
canvas->drawRect(body,
|
|
239
|
+
canvas->drawRect(body, *fill_p);
|
|
141
240
|
}
|
|
142
241
|
|
|
143
|
-
const bool draw_border = bull ? draw_border_bull : draw_border_bear;
|
|
144
242
|
// Inset by half the stroke so the centered stroke falls fully within the
|
|
145
243
|
// body: the border overlaps the fill's outer edge instead of straddling
|
|
146
244
|
// it, keeping the candle's footprint exactly body_w. Skip when the body is
|
|
@@ -148,13 +246,12 @@ void draw(SkCanvas* canvas,
|
|
|
148
246
|
if (draw_border && body_w > kBorderWidthPx && h > kBorderWidthPx) {
|
|
149
247
|
const SkRect inner =
|
|
150
248
|
body.makeInset(kBorderWidthPx * 0.5f, kBorderWidthPx * 0.5f);
|
|
151
|
-
const SkPaint& bp = bull ? border_bull : border_bear;
|
|
152
249
|
// Concentric radius so the inside border follows the rounded body.
|
|
153
250
|
const float ir = std::max(0.f, r - kBorderWidthPx * 0.5f);
|
|
154
251
|
if (ir > 0.f) {
|
|
155
|
-
canvas->drawRoundRect(inner, ir, ir,
|
|
252
|
+
canvas->drawRoundRect(inner, ir, ir, *border_p);
|
|
156
253
|
} else {
|
|
157
|
-
canvas->drawRect(inner,
|
|
254
|
+
canvas->drawRect(inner, *border_p);
|
|
158
255
|
}
|
|
159
256
|
}
|
|
160
257
|
}
|
package/cpp/_core_src/candles.h
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
class SkCanvas;
|
|
13
13
|
|
|
14
14
|
namespace vroom {
|
|
15
|
+
struct CandleSnapshot;
|
|
15
16
|
struct Layout;
|
|
16
17
|
struct PriceBounds;
|
|
17
18
|
struct Theme;
|
|
@@ -26,6 +27,12 @@ namespace vroom::candles {
|
|
|
26
27
|
// candle→line transition: 0 = normal candle, 1 = a flat point at the close (the
|
|
27
28
|
// body/wick heights and width shrink to the line). `opacity` (0..1) fades the
|
|
28
29
|
// whole candle layer out as the line fades in. Both default to a no-op.
|
|
30
|
+
//
|
|
31
|
+
// `from` / `from_n` is the outgoing geometry of an interval morph, indexed from
|
|
32
|
+
// the right of the visible slice (slot 0 = newest), and `morph_t` (0..1) is the
|
|
33
|
+
// eased progress toward `visible`. Each slot's wick and body interpolate between
|
|
34
|
+
// the two; slots present on only one side fade in or out. `morph_t == 1` (the
|
|
35
|
+
// default) draws `visible` alone.
|
|
29
36
|
void draw(SkCanvas* canvas,
|
|
30
37
|
const ::VroomCandle* visible,
|
|
31
38
|
std::size_t n,
|
|
@@ -36,6 +43,9 @@ void draw(SkCanvas* canvas,
|
|
|
36
43
|
int64_t visible_start_ms,
|
|
37
44
|
int64_t candle_duration_ms,
|
|
38
45
|
float collapse = 0.f,
|
|
39
|
-
float opacity = 1.f
|
|
46
|
+
float opacity = 1.f,
|
|
47
|
+
const CandleSnapshot* from = nullptr,
|
|
48
|
+
std::size_t from_n = 0,
|
|
49
|
+
float morph_t = 1.f);
|
|
40
50
|
|
|
41
51
|
} // namespace vroom::candles
|