react-native-vroom-chart 0.1.4 → 0.4.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 +76 -1
- package/cpp/_core_include/vroom/vroom_chart.h +126 -11
- package/cpp/_core_src/candles.cpp +75 -10
- package/cpp/_core_src/candles.h +8 -1
- package/cpp/_core_src/chart.cpp +28 -3
- package/cpp/_core_src/chart.h +45 -4
- package/cpp/_core_src/chart_facade.cpp +207 -4
- package/cpp/_core_src/drawings.cpp +313 -15
- package/cpp/_core_src/drawings.h +23 -0
- package/cpp/_core_src/liquidity.cpp +112 -0
- package/cpp/_core_src/liquidity.h +35 -0
- package/cpp/_core_src/ma_overlay.cpp +9 -1
- package/cpp/_core_src/ma_overlay.h +2 -1
- package/cpp/_core_src/theme.cpp +5 -0
- package/cpp/_core_src/volume.cpp +15 -3
- package/lib/index.d.mts +179 -15
- package/lib/index.d.ts +179 -15
- package/lib/index.js +96 -4
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +96 -4
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +71 -0
- package/src/index.ts +1 -0
- package/src/jsi.d.ts +15 -0
- package/src/theme.ts +31 -6
- package/src/types.ts +1 -0
- package/src/useChartCore.ts +24 -1
|
@@ -27,11 +27,15 @@ ChartHostObject::~ChartHostObject() {
|
|
|
27
27
|
std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
|
|
28
28
|
jsi::Runtime& rt) {
|
|
29
29
|
std::vector<jsi::PropNameID> out;
|
|
30
|
-
out.reserve(
|
|
30
|
+
out.reserve(24);
|
|
31
31
|
out.push_back(jsi::PropNameID::forAscii(rt, "setCandles"));
|
|
32
32
|
out.push_back(jsi::PropNameID::forAscii(rt, "setSize"));
|
|
33
33
|
out.push_back(jsi::PropNameID::forAscii(rt, "setColor"));
|
|
34
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setFloat"));
|
|
34
35
|
out.push_back(jsi::PropNameID::forAscii(rt, "setVisibleRange"));
|
|
36
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setDefaultCandleWidth"));
|
|
37
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setChartType"));
|
|
38
|
+
out.push_back(jsi::PropNameID::forAscii(rt, "setMorph"));
|
|
35
39
|
out.push_back(jsi::PropNameID::forAscii(rt, "pan"));
|
|
36
40
|
out.push_back(jsi::PropNameID::forAscii(rt, "translate"));
|
|
37
41
|
out.push_back(jsi::PropNameID::forAscii(rt, "zoom"));
|
|
@@ -130,6 +134,25 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
130
134
|
});
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
if (name == "setFloat") {
|
|
138
|
+
// setFloat(keyIndex, value) — keyIndex is a VroomFloatKey (e.g. wick width).
|
|
139
|
+
// The core bounds-checks the key and marks dirty.
|
|
140
|
+
return jsi::Function::createFromHostFunction(
|
|
141
|
+
rt,
|
|
142
|
+
jsi::PropNameID::forAscii(rt, "setFloat"),
|
|
143
|
+
2,
|
|
144
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
145
|
+
const jsi::Value& /*thisVal*/,
|
|
146
|
+
const jsi::Value* args,
|
|
147
|
+
size_t count) -> jsi::Value {
|
|
148
|
+
if (count < 2) return jsi::Value::undefined();
|
|
149
|
+
const int key = static_cast<int>(args[0].asNumber());
|
|
150
|
+
vroom_chart_set_float(chart_, static_cast<VroomFloatKey>(key),
|
|
151
|
+
static_cast<float>(args[1].asNumber()));
|
|
152
|
+
return jsi::Value::undefined();
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
133
156
|
if (name == "setVisibleRange") {
|
|
134
157
|
// Pass 0,0 to clear (show all).
|
|
135
158
|
// Timestamps are Number on the JS side — fine for ms-since-epoch since
|
|
@@ -150,6 +173,58 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
150
173
|
});
|
|
151
174
|
}
|
|
152
175
|
|
|
176
|
+
if (name == "setDefaultCandleWidth") {
|
|
177
|
+
// setDefaultCandleWidth(px) — target candle body width for initial framing.
|
|
178
|
+
return jsi::Function::createFromHostFunction(
|
|
179
|
+
rt,
|
|
180
|
+
jsi::PropNameID::forAscii(rt, "setDefaultCandleWidth"),
|
|
181
|
+
1,
|
|
182
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
183
|
+
const jsi::Value& /*thisVal*/,
|
|
184
|
+
const jsi::Value* args,
|
|
185
|
+
size_t count) -> jsi::Value {
|
|
186
|
+
if (count < 1) return jsi::Value::undefined();
|
|
187
|
+
vroom_chart_set_default_candle_width(
|
|
188
|
+
chart_, static_cast<float>(args[0].asNumber()));
|
|
189
|
+
return jsi::Value::undefined();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (name == "setChartType") {
|
|
194
|
+
// setChartType(mode) — 0 = candlesticks (default), 1 = line chart.
|
|
195
|
+
return jsi::Function::createFromHostFunction(
|
|
196
|
+
rt,
|
|
197
|
+
jsi::PropNameID::forAscii(rt, "setChartType"),
|
|
198
|
+
1,
|
|
199
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
200
|
+
const jsi::Value& /*thisVal*/,
|
|
201
|
+
const jsi::Value* args,
|
|
202
|
+
size_t count) -> jsi::Value {
|
|
203
|
+
if (count < 1) return jsi::Value::undefined();
|
|
204
|
+
vroom_chart_set_chart_type(
|
|
205
|
+
chart_, static_cast<int32_t>(args[0].asNumber()));
|
|
206
|
+
return jsi::Value::undefined();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (name == "setMorph") {
|
|
211
|
+
// setMorph(collapse, fade) — candle↔line blend for animated transitions.
|
|
212
|
+
return jsi::Function::createFromHostFunction(
|
|
213
|
+
rt,
|
|
214
|
+
jsi::PropNameID::forAscii(rt, "setMorph"),
|
|
215
|
+
2,
|
|
216
|
+
[this](jsi::Runtime& /*rt2*/,
|
|
217
|
+
const jsi::Value& /*thisVal*/,
|
|
218
|
+
const jsi::Value* args,
|
|
219
|
+
size_t count) -> jsi::Value {
|
|
220
|
+
if (count < 2) return jsi::Value::undefined();
|
|
221
|
+
vroom_chart_set_morph(chart_,
|
|
222
|
+
static_cast<float>(args[0].asNumber()),
|
|
223
|
+
static_cast<float>(args[1].asNumber()));
|
|
224
|
+
return jsi::Value::undefined();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
153
228
|
if (name == "pan") {
|
|
154
229
|
// pan(dx, dy) -> JsiSkPicture
|
|
155
230
|
// Mutates the visible range and renders in one JSI call so gesture
|
|
@@ -55,14 +55,49 @@ typedef struct VroomDrawPoint {
|
|
|
55
55
|
double price;
|
|
56
56
|
} VroomDrawPoint;
|
|
57
57
|
|
|
58
|
-
// A committed
|
|
58
|
+
// A committed drawing on the price pane. `kind` selects the geometry:
|
|
59
|
+
// 0 = line — a two-point trendline from `a` to `b`.
|
|
60
|
+
// 1 = box — an axis-aligned rectangle whose two opposite corners are `a`
|
|
61
|
+
// and `b`; the other two corners (a.x,b.y) and (b.x,a.y) derive.
|
|
62
|
+
// 2 = pencil — a freehand path through `points` (in draw order). `a`/`b` mirror
|
|
63
|
+
// the first/last point so bounds and handle code stay uniform.
|
|
64
|
+
//
|
|
65
|
+
// `points`/`point_count` are only read for kind 2; line and box leave them
|
|
66
|
+
// null/0. Like the rest of this API the points are copied internally, so the
|
|
67
|
+
// caller may free them as soon as the call returns.
|
|
59
68
|
typedef struct VroomDrawing {
|
|
60
|
-
VroomDrawPoint
|
|
61
|
-
VroomDrawPoint
|
|
62
|
-
uint32_t
|
|
63
|
-
float
|
|
69
|
+
VroomDrawPoint a;
|
|
70
|
+
VroomDrawPoint b;
|
|
71
|
+
uint32_t color; // 0xAARRGGBB
|
|
72
|
+
float width; // stroke width in px
|
|
73
|
+
int32_t kind; // 0 = line, 1 = box, 2 = pencil
|
|
74
|
+
const VroomDrawPoint* points; // pencil path (kind 2), else null
|
|
75
|
+
int32_t point_count; // number of `points`, else 0
|
|
64
76
|
} VroomDrawing;
|
|
65
77
|
|
|
78
|
+
// A resting-liquidity band: a price interval carrying a total order size on one
|
|
79
|
+
// side of the book. Anchored in price space, so it scales with the y-axis.
|
|
80
|
+
typedef struct VroomBand {
|
|
81
|
+
double min_price; // bottom of the interval
|
|
82
|
+
double max_price; // top of the interval
|
|
83
|
+
int32_t side; // 0 = buy, 1 = sell (selects the color)
|
|
84
|
+
double volume; // total resting size; drives the band's opacity
|
|
85
|
+
} VroomBand;
|
|
86
|
+
|
|
87
|
+
// Style shared by all liquidity bands. `buy_color`/`sell_color` are 0xAARRGGBB;
|
|
88
|
+
// the alpha is replaced per band by its volume mapped into
|
|
89
|
+
// [min_opacity, max_opacity]. `max_volume <= 0` auto-scales to the largest band.
|
|
90
|
+
// The leftward reach is min(width_px, width_frac * pane_width).
|
|
91
|
+
typedef struct VroomLiquidityStyle {
|
|
92
|
+
uint32_t buy_color;
|
|
93
|
+
uint32_t sell_color;
|
|
94
|
+
double max_volume;
|
|
95
|
+
float min_opacity;
|
|
96
|
+
float max_opacity;
|
|
97
|
+
float width_px;
|
|
98
|
+
float width_frac;
|
|
99
|
+
} VroomLiquidityStyle;
|
|
100
|
+
|
|
66
101
|
// A continuous data coordinate at a pixel position (no candle snapping). Used to
|
|
67
102
|
// translate a drawing-tool click into a data-space anchor.
|
|
68
103
|
typedef struct VroomCoord {
|
|
@@ -89,6 +124,7 @@ typedef enum {
|
|
|
89
124
|
VROOM_COLOR_WICK_BEAR, // bear wick; 0 alpha => inherit BEAR fill
|
|
90
125
|
VROOM_COLOR_ACCENT_BULL, // generic up color: price indicator, volume, MACD
|
|
91
126
|
VROOM_COLOR_ACCENT_BEAR, // generic down color
|
|
127
|
+
VROOM_COLOR_LINE, // line-chart-mode close-price polyline
|
|
92
128
|
VROOM_COLOR_COUNT_
|
|
93
129
|
} VroomColorKey;
|
|
94
130
|
|
|
@@ -101,6 +137,10 @@ typedef enum {
|
|
|
101
137
|
VROOM_FLOAT_X_AXIS_HEIGHT_PX, // bottom strip reserved for time labels
|
|
102
138
|
VROOM_FLOAT_VOLUME_OPACITY, // volume bar opacity (1=opaque)
|
|
103
139
|
VROOM_FLOAT_INDICATOR_HEIGHT_FRAC, // below-chart indicator pane, fraction of height
|
|
140
|
+
VROOM_FLOAT_CANDLE_RADIUS_PX, // candle body corner radius px (0 = square)
|
|
141
|
+
VROOM_FLOAT_WICK_ROUND_CAP, // 0/1: round the wick end caps
|
|
142
|
+
VROOM_FLOAT_VOLUME_RADIUS_PX, // volume bar top-corner radius px (0 = square)
|
|
143
|
+
VROOM_FLOAT_LINE_WIDTH_PX, // line-chart-mode polyline stroke width px
|
|
104
144
|
VROOM_FLOAT_COUNT_
|
|
105
145
|
} VroomFloatKey;
|
|
106
146
|
|
|
@@ -131,6 +171,23 @@ void vroom_chart_set_size(VroomChart* chart, float width_px, float height_px, fl
|
|
|
131
171
|
|
|
132
172
|
void vroom_chart_set_visible_range(VroomChart* chart, int64_t start_ms, int64_t end_ms);
|
|
133
173
|
|
|
174
|
+
// Target candle *body* width in px for the default/reset framing. Larger = more
|
|
175
|
+
// zoomed in (fewer candles), smaller = more zoomed out. 0 restores the legacy
|
|
176
|
+
// "most recent ~80 candles" default. Affects initial framing only; an explicit
|
|
177
|
+
// set_visible_range still overrides it.
|
|
178
|
+
void vroom_chart_set_default_candle_width(VroomChart* chart, float px);
|
|
179
|
+
|
|
180
|
+
// Chart render mode: 0 = candlesticks (default), 1 = line chart (a polyline
|
|
181
|
+
// through each candle's close). Other layers (volume, indicators, overlays,
|
|
182
|
+
// crosshair, drawings) are unaffected.
|
|
183
|
+
void vroom_chart_set_chart_type(VroomChart* chart, int32_t mode);
|
|
184
|
+
|
|
185
|
+
// Candle↔line morph blend for animated transitions. `collapse` folds candles
|
|
186
|
+
// toward their close price; `fade` crossfades candles→line. Both 0 = candles,
|
|
187
|
+
// both 1 = line. Driven per-frame by the host animation loop; set_chart_type
|
|
188
|
+
// snaps both to the target.
|
|
189
|
+
void vroom_chart_set_morph(VroomChart* chart, float collapse, float fade);
|
|
190
|
+
|
|
134
191
|
// Reads the current visible time window. Either out pointer may be null.
|
|
135
192
|
// Both are 0 when the window is still uninitialized.
|
|
136
193
|
void vroom_chart_get_visible_range(VroomChart* chart,
|
|
@@ -272,16 +329,67 @@ void vroom_chart_set_vwap(VroomChart* chart, bool enabled, int reset_offset_min,
|
|
|
272
329
|
void vroom_chart_set_drawings(VroomChart* chart, const VroomDrawing* drawings,
|
|
273
330
|
size_t count);
|
|
274
331
|
|
|
332
|
+
// Hit-tests pixel (x_px, y_px) against the committed drawings. On a hit, fills
|
|
333
|
+
// *out_index with the drawing index, *out_part with 0 (endpoint A), 1
|
|
334
|
+
// (endpoint B), or 2 (line body), *out_t with the 0..1 grab position along the
|
|
335
|
+
// segment (A→B; 0/1 for handle hits), and returns true. Endpoint hits are only
|
|
336
|
+
// reported for the currently selected drawing (whose handles are visible).
|
|
337
|
+
// Returns false on a miss (out params untouched). Any out pointer may be null.
|
|
338
|
+
bool vroom_chart_hit_test_drawing(VroomChart* chart, float x_px, float y_px,
|
|
339
|
+
int32_t* out_index, int32_t* out_part,
|
|
340
|
+
float* out_t);
|
|
341
|
+
|
|
342
|
+
// Selects a committed drawing (renders its endpoint handles). `index` -1 clears
|
|
343
|
+
// the selection. `grabbed_endpoint` 0/1 renders that handle 50% larger while it's
|
|
344
|
+
// being dragged; -1 for none. An out-of-range index clears the selection.
|
|
345
|
+
void vroom_chart_set_selected_drawing(VroomChart* chart, int32_t index,
|
|
346
|
+
int32_t grabbed_endpoint);
|
|
347
|
+
|
|
348
|
+
// Moves one endpoint of a committed drawing to a new data-space anchor (for live
|
|
349
|
+
// handle dragging). `endpoint` is 0 (A) or 1 (B). No-op for an out-of-range index.
|
|
350
|
+
void vroom_chart_move_drawing_endpoint(VroomChart* chart, int32_t index,
|
|
351
|
+
int32_t endpoint, int64_t time_ms,
|
|
352
|
+
double price);
|
|
353
|
+
|
|
354
|
+
// Shifts a whole committed drawing by a *relative* data-space delta — `a`, `b`,
|
|
355
|
+
// and (for a pencil) every path point. Used for live body dragging of shapes
|
|
356
|
+
// whose points can't be restated cheaply. No-op for an out-of-range index.
|
|
357
|
+
void vroom_chart_translate_drawing(VroomChart* chart, int32_t index,
|
|
358
|
+
int64_t d_time_ms, double d_price);
|
|
359
|
+
|
|
360
|
+
// ---- Liquidity bands (order-book depth overlay) ---------------------------
|
|
361
|
+
|
|
362
|
+
// Replaces the full set of resting-liquidity bands and their shared style.
|
|
363
|
+
// Bands render behind the candles, anchored at the inner edge of the price axis
|
|
364
|
+
// and fading left. Pass count 0 to clear; `style` may be null when count is 0.
|
|
365
|
+
void vroom_chart_set_liquidity(VroomChart* chart, const VroomBand* bands,
|
|
366
|
+
size_t count, const VroomLiquidityStyle* style);
|
|
367
|
+
|
|
275
368
|
// Sets the transient in-progress "draft" the drawing tool shows while the user
|
|
276
369
|
// places points. Node A is always shown; when `has_b`, node B is shown too.
|
|
277
|
-
// `guide != 0` also draws the
|
|
278
|
-
// draws node dots only (the committed
|
|
279
|
-
//
|
|
370
|
+
// `guide != 0` also draws the live preview (a guideline for a line, or a preview
|
|
371
|
+
// rectangle for a box); `guide == 0` draws node dots only (the committed shape
|
|
372
|
+
// already renders via set_drawings). `kind` matches VroomDrawing (0 = line,
|
|
373
|
+
// 1 = box) so the preview geometry matches the eventual shape. `color`/`width`
|
|
374
|
+
// style the preview to match the eventual drawing.
|
|
280
375
|
void vroom_chart_set_draft(VroomChart* chart, int64_t a_time, double a_price,
|
|
281
376
|
bool has_b, int64_t b_time, double b_price,
|
|
282
|
-
bool guide, uint32_t color, float width
|
|
283
|
-
|
|
284
|
-
|
|
377
|
+
bool guide, uint32_t color, float width,
|
|
378
|
+
int32_t kind);
|
|
379
|
+
|
|
380
|
+
// Begins a freehand (pencil) draft stroke, clearing any previous draft points.
|
|
381
|
+
// Follow with vroom_chart_append_draft_point per captured sample; the growing
|
|
382
|
+
// path renders live. `color`/`width` style it to match the eventual stroke.
|
|
383
|
+
void vroom_chart_start_draft_stroke(VroomChart* chart, uint32_t color,
|
|
384
|
+
float width);
|
|
385
|
+
|
|
386
|
+
// Appends one point to the in-progress freehand draft. Cheap (O(1) amortized) so
|
|
387
|
+
// it can be called on every pointer move without restating the whole path.
|
|
388
|
+
// No-op unless a draft stroke was started.
|
|
389
|
+
void vroom_chart_append_draft_point(VroomChart* chart, int64_t time_ms,
|
|
390
|
+
double price);
|
|
391
|
+
|
|
392
|
+
// Clears the draft (hides the in-progress node dots / guideline / stroke).
|
|
285
393
|
void vroom_chart_clear_draft(VroomChart* chart);
|
|
286
394
|
|
|
287
395
|
// Fills *out with the continuous data coordinate (time_ms, price) at pixel
|
|
@@ -291,6 +399,13 @@ void vroom_chart_clear_draft(VroomChart* chart);
|
|
|
291
399
|
bool vroom_chart_coord_at(VroomChart* chart, float x_px, float y_px,
|
|
292
400
|
VroomCoord* out);
|
|
293
401
|
|
|
402
|
+
// Projects a data coordinate (time_ms, price) to its pixel position, filling
|
|
403
|
+
// *out_x / *out_y (either may be null) with the free (non-snapped) mapping — the
|
|
404
|
+
// inverse of vroom_chart_coord_at, matching the rendered drawing geometry.
|
|
405
|
+
// Returns false (out params untouched) when there are no candles / degenerate.
|
|
406
|
+
bool vroom_chart_project(VroomChart* chart, int64_t time_ms, double price,
|
|
407
|
+
float* out_x, float* out_y);
|
|
408
|
+
|
|
294
409
|
// ---- Rendering ------------------------------------------------------------
|
|
295
410
|
|
|
296
411
|
void vroom_chart_draw(VroomChart* chart, SkCanvas* canvas);
|
|
@@ -30,15 +30,44 @@ void draw(SkCanvas* canvas,
|
|
|
30
30
|
const PriceBounds& bounds,
|
|
31
31
|
int64_t window_ms,
|
|
32
32
|
int64_t visible_start_ms,
|
|
33
|
-
int64_t candle_duration_ms
|
|
33
|
+
int64_t candle_duration_ms,
|
|
34
|
+
float collapse,
|
|
35
|
+
float opacity) {
|
|
34
36
|
if (!canvas || n == 0) return;
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
collapse = std::clamp(collapse, 0.f, 1.f);
|
|
39
|
+
opacity = std::clamp(opacity, 0.f, 1.f);
|
|
40
|
+
if (opacity <= 0.f) return;
|
|
41
|
+
|
|
42
|
+
// During the candle→line morph, fade the entire candle layer as one unit so
|
|
43
|
+
// overlapping bars/wicks composite cleanly instead of stacking alpha.
|
|
44
|
+
const bool fade_layer = opacity < 0.999f;
|
|
45
|
+
if (fade_layer) {
|
|
46
|
+
canvas->saveLayerAlpha(nullptr,
|
|
47
|
+
static_cast<U8CPU>(opacity * 255.f + 0.5f));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const float full_body_w = vroom::candle_body_width(
|
|
37
51
|
lay, window_ms, candle_duration_ms);
|
|
52
|
+
// Thin the body toward the line stroke width as candles collapse to the line.
|
|
53
|
+
const float line_w = theme.floats[VROOM_FLOAT_LINE_WIDTH_PX];
|
|
54
|
+
const float body_w = full_body_w + (line_w - full_body_w) * collapse;
|
|
38
55
|
|
|
39
56
|
const uint32_t fill_bull = theme.colors[VROOM_COLOR_BULL];
|
|
40
57
|
const uint32_t fill_bear = theme.colors[VROOM_COLOR_BEAR];
|
|
41
58
|
|
|
59
|
+
const float body_r = theme.floats[VROOM_FLOAT_CANDLE_RADIUS_PX];
|
|
60
|
+
const bool wick_round = theme.floats[VROOM_FLOAT_WICK_ROUND_CAP] > 0.5f;
|
|
61
|
+
|
|
62
|
+
constexpr float kBorderWidthPx = 1.f;
|
|
63
|
+
// Draw the border only when it differs from the fill; an inherited/transparent
|
|
64
|
+
// border color (the default, and the "hide" path) renders nothing — so a hidden
|
|
65
|
+
// border costs no pixels and never affects candle width.
|
|
66
|
+
const bool draw_border_bull =
|
|
67
|
+
resolve_color(theme.colors[VROOM_COLOR_BORDER_BULL], fill_bull) != fill_bull;
|
|
68
|
+
const bool draw_border_bear =
|
|
69
|
+
resolve_color(theme.colors[VROOM_COLOR_BORDER_BEAR], fill_bear) != fill_bear;
|
|
70
|
+
|
|
42
71
|
SkPaint bull_paint;
|
|
43
72
|
bull_paint.setAntiAlias(true);
|
|
44
73
|
bull_paint.setColor(fill_bull);
|
|
@@ -53,17 +82,19 @@ void draw(SkCanvas* canvas,
|
|
|
53
82
|
resolve_color(theme.colors[VROOM_COLOR_WICK_BULL], fill_bull));
|
|
54
83
|
wick_bull.setStrokeWidth(theme.floats[VROOM_FLOAT_WICK_WIDTH_PX]);
|
|
55
84
|
wick_bull.setStyle(SkPaint::kStroke_Style);
|
|
85
|
+
wick_bull.setStrokeCap(wick_round ? SkPaint::kRound_Cap : SkPaint::kButt_Cap);
|
|
56
86
|
|
|
57
87
|
SkPaint wick_bear = wick_bull;
|
|
58
88
|
wick_bear.setColor(
|
|
59
89
|
resolve_color(theme.colors[VROOM_COLOR_WICK_BEAR], fill_bear));
|
|
60
90
|
|
|
61
|
-
// 1px body outlines
|
|
62
|
-
// border
|
|
91
|
+
// 1px body outlines, drawn *inside* the body (see the inset at draw time) so
|
|
92
|
+
// a contrasting border never widens the candle. Default sentinel resolves to
|
|
93
|
+
// the fill color; such borders are skipped entirely (draw_border_* above).
|
|
63
94
|
SkPaint border_bull;
|
|
64
95
|
border_bull.setAntiAlias(true);
|
|
65
96
|
border_bull.setStyle(SkPaint::kStroke_Style);
|
|
66
|
-
border_bull.setStrokeWidth(
|
|
97
|
+
border_bull.setStrokeWidth(kBorderWidthPx);
|
|
67
98
|
border_bull.setColor(
|
|
68
99
|
resolve_color(theme.colors[VROOM_COLOR_BORDER_BULL], fill_bull));
|
|
69
100
|
|
|
@@ -80,10 +111,18 @@ void draw(SkCanvas* canvas,
|
|
|
80
111
|
const float cx = vroom::candle_center_x(
|
|
81
112
|
lay, c.time_ms, candle_duration_ms,
|
|
82
113
|
visible_start_ms, window_ms);
|
|
83
|
-
const float y_high = vroom::price_to_y(lay, bounds, c.high);
|
|
84
|
-
const float y_low = vroom::price_to_y(lay, bounds, c.low);
|
|
85
|
-
const float y_open = vroom::price_to_y(lay, bounds, c.open);
|
|
86
114
|
const float y_close = vroom::price_to_y(lay, bounds, c.close);
|
|
115
|
+
// Collapse high/low/open toward the close so the candle folds into its
|
|
116
|
+
// close point (the line vertex) as `collapse` → 1.
|
|
117
|
+
const float y_high =
|
|
118
|
+
vroom::price_to_y(lay, bounds, c.high) * (1.f - collapse) +
|
|
119
|
+
y_close * collapse;
|
|
120
|
+
const float y_low =
|
|
121
|
+
vroom::price_to_y(lay, bounds, c.low) * (1.f - collapse) +
|
|
122
|
+
y_close * collapse;
|
|
123
|
+
const float y_open =
|
|
124
|
+
vroom::price_to_y(lay, bounds, c.open) * (1.f - collapse) +
|
|
125
|
+
y_close * collapse;
|
|
87
126
|
|
|
88
127
|
canvas->drawLine(cx, y_high, cx, y_low,
|
|
89
128
|
bull ? wick_bull : wick_bear);
|
|
@@ -92,9 +131,35 @@ void draw(SkCanvas* canvas,
|
|
|
92
131
|
const float y_bot = std::max(y_open, y_close);
|
|
93
132
|
const float h = std::max(1.f, y_bot - y_top);
|
|
94
133
|
const SkRect body = SkRect::MakeXYWH(cx - half_body, y_top, body_w, h);
|
|
95
|
-
|
|
96
|
-
|
|
134
|
+
// Corner radius clamped so thin candles / short bodies don't over-round.
|
|
135
|
+
const float r = std::min({body_r, body_w * 0.5f, h * 0.5f});
|
|
136
|
+
const SkPaint& fill = bull ? bull_paint : bear_paint;
|
|
137
|
+
if (r > 0.f) {
|
|
138
|
+
canvas->drawRoundRect(body, r, r, fill);
|
|
139
|
+
} else {
|
|
140
|
+
canvas->drawRect(body, fill);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const bool draw_border = bull ? draw_border_bull : draw_border_bear;
|
|
144
|
+
// Inset by half the stroke so the centered stroke falls fully within the
|
|
145
|
+
// body: the border overlaps the fill's outer edge instead of straddling
|
|
146
|
+
// it, keeping the candle's footprint exactly body_w. Skip when the body is
|
|
147
|
+
// too small to hold it.
|
|
148
|
+
if (draw_border && body_w > kBorderWidthPx && h > kBorderWidthPx) {
|
|
149
|
+
const SkRect inner =
|
|
150
|
+
body.makeInset(kBorderWidthPx * 0.5f, kBorderWidthPx * 0.5f);
|
|
151
|
+
const SkPaint& bp = bull ? border_bull : border_bear;
|
|
152
|
+
// Concentric radius so the inside border follows the rounded body.
|
|
153
|
+
const float ir = std::max(0.f, r - kBorderWidthPx * 0.5f);
|
|
154
|
+
if (ir > 0.f) {
|
|
155
|
+
canvas->drawRoundRect(inner, ir, ir, bp);
|
|
156
|
+
} else {
|
|
157
|
+
canvas->drawRect(inner, bp);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
97
160
|
}
|
|
161
|
+
|
|
162
|
+
if (fade_layer) canvas->restore();
|
|
98
163
|
}
|
|
99
164
|
|
|
100
165
|
} // namespace vroom::candles
|
package/cpp/_core_src/candles.h
CHANGED
|
@@ -21,6 +21,11 @@ namespace vroom::candles {
|
|
|
21
21
|
|
|
22
22
|
// Draws every candle in [visible, visible + n). The visible slice should
|
|
23
23
|
// already be filtered by `visible_indices` in viewport.h.
|
|
24
|
+
//
|
|
25
|
+
// `collapse` (0..1) morphs each candle vertically toward its close price for the
|
|
26
|
+
// candle→line transition: 0 = normal candle, 1 = a flat point at the close (the
|
|
27
|
+
// body/wick heights and width shrink to the line). `opacity` (0..1) fades the
|
|
28
|
+
// whole candle layer out as the line fades in. Both default to a no-op.
|
|
24
29
|
void draw(SkCanvas* canvas,
|
|
25
30
|
const ::VroomCandle* visible,
|
|
26
31
|
std::size_t n,
|
|
@@ -29,6 +34,8 @@ void draw(SkCanvas* canvas,
|
|
|
29
34
|
const PriceBounds& bounds,
|
|
30
35
|
int64_t window_ms,
|
|
31
36
|
int64_t visible_start_ms,
|
|
32
|
-
int64_t candle_duration_ms
|
|
37
|
+
int64_t candle_duration_ms,
|
|
38
|
+
float collapse = 0.f,
|
|
39
|
+
float opacity = 1.f);
|
|
33
40
|
|
|
34
41
|
} // namespace vroom::candles
|
package/cpp/_core_src/chart.cpp
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
#include "crosshair.h"
|
|
24
24
|
#include "drawings.h"
|
|
25
25
|
#include "labels.h"
|
|
26
|
+
#include "liquidity.h"
|
|
26
27
|
#include "ma.h"
|
|
27
28
|
#include "ma_overlay.h"
|
|
28
29
|
#include "macd.h"
|
|
@@ -142,9 +143,33 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
142
143
|
vroom::volume::draw(canvas, visible, n, lay, theme,
|
|
143
144
|
window_ms, visible_start_ms, candle_duration_ms);
|
|
144
145
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
// 4.6. Liquidity bands (resting-order depth) — behind the candles so the
|
|
147
|
+
// candle bodies paint over the volume-driven tint. Anchored in price
|
|
148
|
+
// space, so they scale with the y-axis.
|
|
149
|
+
vroom::liquidity::draw(canvas, *this, lay, bounds, candle_right,
|
|
150
|
+
candle_area_h);
|
|
151
|
+
|
|
152
|
+
// 5. Price series — candles, a close-price line, or a blend of the two during
|
|
153
|
+
// the candle↔line morph. `morph_fade` crossfades candles→line and
|
|
154
|
+
// `morph_collapse` folds each candle toward its close (the line vertex).
|
|
155
|
+
// fade 0 = pure candles, fade 1 = pure line. The line reuses the MA-overlay
|
|
156
|
+
// polyline routine, fed the visible closes and styled by theme.LINE.
|
|
157
|
+
const float fade = morph_fade;
|
|
158
|
+
const float collapse = morph_collapse;
|
|
159
|
+
if (fade < 1.f) {
|
|
160
|
+
vroom::candles::draw(canvas, visible, n, lay, theme, bounds, window_ms,
|
|
161
|
+
visible_start_ms, candle_duration_ms, collapse,
|
|
162
|
+
1.f - fade);
|
|
163
|
+
}
|
|
164
|
+
if (fade > 0.f) {
|
|
165
|
+
std::vector<double> closes(n);
|
|
166
|
+
for (std::size_t i = 0; i < n; ++i) closes[i] = visible[i].close;
|
|
167
|
+
vroom::ma_overlay::draw(
|
|
168
|
+
canvas, lay, bounds, visible, n, closes.data(), window_ms,
|
|
169
|
+
visible_start_ms, candle_duration_ms, candle_right, candle_area_h,
|
|
170
|
+
theme.colors[VROOM_COLOR_LINE], theme.floats[VROOM_FLOAT_LINE_WIDTH_PX],
|
|
171
|
+
nullptr, fade);
|
|
172
|
+
}
|
|
148
173
|
|
|
149
174
|
// 5.5. Moving-average overlay lines (SMA/EMA) on the price pane, over the
|
|
150
175
|
// candles. They share the candle price scale and don't reserve a pane.
|
package/cpp/_core_src/chart.h
CHANGED
|
@@ -50,6 +50,19 @@ struct VroomChart {
|
|
|
50
50
|
int64_t visible_start_ms = 0;
|
|
51
51
|
int64_t visible_end_ms = 0;
|
|
52
52
|
|
|
53
|
+
// >0: default/reset framing targets this candle *body* width in px (drives
|
|
54
|
+
// initial zoom). 0 = legacy "last ~80 candles" behavior.
|
|
55
|
+
float default_candle_px = 0.f;
|
|
56
|
+
|
|
57
|
+
// Render mode: 0 = candlesticks (default), 1 = line chart (close polyline).
|
|
58
|
+
int chart_type = 0;
|
|
59
|
+
|
|
60
|
+
// Candle↔line morph blend, driven per-frame by the JS animation loop.
|
|
61
|
+
// `morph_collapse` folds candles toward their close; `morph_fade` crossfades
|
|
62
|
+
// candles→line. Both 0 = candles, both 1 = line. set_chart_type snaps them.
|
|
63
|
+
float morph_collapse = 0.f;
|
|
64
|
+
float morph_fade = 0.f;
|
|
65
|
+
|
|
53
66
|
// Cached y-axis width in pixels, sized to fit the widest formatted price
|
|
54
67
|
// label. 0 = uncomputed; layout() falls back to a width ratio.
|
|
55
68
|
float axis_width_px = 0.f;
|
|
@@ -122,10 +135,22 @@ struct VroomChart {
|
|
|
122
135
|
std::vector<unsigned char> vwap_breaks;
|
|
123
136
|
bool vwap_dirty = true;
|
|
124
137
|
|
|
125
|
-
// --- drawings (
|
|
126
|
-
// Committed
|
|
127
|
-
//
|
|
128
|
-
|
|
138
|
+
// --- drawings (annotations) --------------------------------------------
|
|
139
|
+
// Committed drawings, anchored in data space so they track the candles on
|
|
140
|
+
// pan/zoom. Drawn on the price pane above candles/overlays.
|
|
141
|
+
//
|
|
142
|
+
// Mirrors the public VroomDrawing but *owns* its points, so a pencil path
|
|
143
|
+
// (kind 2) can carry a variable number of them. For line/box `points` is
|
|
144
|
+
// empty and only a/b are used; for pencil a/b mirror the first/last point.
|
|
145
|
+
struct StoredDrawing {
|
|
146
|
+
VroomDrawPoint a{};
|
|
147
|
+
VroomDrawPoint b{};
|
|
148
|
+
uint32_t color = 0xff2962ff;
|
|
149
|
+
float width = 2.f;
|
|
150
|
+
int32_t kind = 0;
|
|
151
|
+
std::vector<VroomDrawPoint> points; // pencil path (kind 2)
|
|
152
|
+
};
|
|
153
|
+
std::vector<StoredDrawing> drawings;
|
|
129
154
|
|
|
130
155
|
// Transient in-progress "draft" the drawing tool shows while placing points.
|
|
131
156
|
// draft_a is always drawn (node dot); draft_b is drawn when draft_has_b.
|
|
@@ -139,6 +164,22 @@ struct VroomChart {
|
|
|
139
164
|
bool draft_guide = false;
|
|
140
165
|
uint32_t draft_color = 0xff2962ff;
|
|
141
166
|
float draft_width = 2.f;
|
|
167
|
+
int32_t draft_kind = 0; // 0 = line, 1 = box, 2 = pencil (VroomDrawing)
|
|
168
|
+
// Freehand stroke in progress (draft_kind 2), grown one point at a time.
|
|
169
|
+
std::vector<VroomDrawPoint> draft_points;
|
|
170
|
+
|
|
171
|
+
// Selection/editing state for committed drawings. selected_drawing indexes
|
|
172
|
+
// `drawings` (or -1); its endpoints render as handles. grabbed_endpoint is
|
|
173
|
+
// 0 (A) or 1 (B) while that handle is being dragged (rendered 50% larger),
|
|
174
|
+
// else -1.
|
|
175
|
+
int32_t selected_drawing = -1;
|
|
176
|
+
int32_t grabbed_endpoint = -1;
|
|
177
|
+
|
|
178
|
+
// --- liquidity bands (order-book depth overlay) ------------------------
|
|
179
|
+
// Resting-order bands anchored in price space, drawn behind the candles and
|
|
180
|
+
// fading left from the price axis. Empty when the overlay is off.
|
|
181
|
+
std::vector<VroomBand> bands;
|
|
182
|
+
VroomLiquidityStyle liquidity_style{};
|
|
142
183
|
|
|
143
184
|
// --- theme --------------------------------------------------------------
|
|
144
185
|
vroom::Theme theme;
|