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.
Files changed (58) hide show
  1. package/android/CMakeLists.txt +143 -0
  2. package/android/README.md +60 -5
  3. package/android/build.gradle +115 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/cpp/VroomChartJni.cpp +24 -0
  6. package/android/src/main/java/com/vroom/chart/VroomChartModule.kt +31 -0
  7. package/android/src/main/java/com/vroom/chart/VroomChartPackage.kt +31 -0
  8. package/cpp/VroomChartHostObject.cpp +368 -33
  9. package/cpp/VroomJsiInstaller.cpp +11 -1
  10. package/cpp/VroomSkiaContext.cpp +15 -7
  11. package/cpp/_core_include/vroom/vroom_chart.h +250 -22
  12. package/cpp/_core_src/bollinger.h +1 -1
  13. package/cpp/_core_src/candles.cpp +138 -41
  14. package/cpp/_core_src/candles.h +11 -1
  15. package/cpp/_core_src/chart.cpp +98 -40
  16. package/cpp/_core_src/chart.h +82 -20
  17. package/cpp/_core_src/chart_facade.cpp +297 -66
  18. package/cpp/_core_src/gradient.cpp +46 -0
  19. package/cpp/_core_src/gradient.h +31 -0
  20. package/cpp/_core_src/labels.cpp +49 -16
  21. package/cpp/_core_src/labels.h +33 -4
  22. package/cpp/_core_src/liquidity.cpp +3 -37
  23. package/cpp/_core_src/ma_overlay.cpp +174 -12
  24. package/cpp/_core_src/ma_overlay.h +55 -3
  25. package/cpp/_core_src/macd.cpp +13 -42
  26. package/cpp/_core_src/macd.h +11 -8
  27. package/cpp/_core_src/macd_pane.cpp +57 -27
  28. package/cpp/_core_src/price_line_layout.cpp +87 -0
  29. package/cpp/_core_src/price_line_layout.h +80 -0
  30. package/cpp/_core_src/price_lines.cpp +428 -0
  31. package/cpp/_core_src/price_lines.h +56 -0
  32. package/cpp/_core_src/rsi.cpp +4 -18
  33. package/cpp/_core_src/rsi.h +6 -6
  34. package/cpp/_core_src/rsi_pane.cpp +34 -19
  35. package/cpp/_core_src/series_ma.cpp +64 -0
  36. package/cpp/_core_src/series_ma.h +30 -0
  37. package/cpp/_core_src/style_inherit.h +35 -0
  38. package/cpp/_core_src/theme.cpp +2 -1
  39. package/cpp/_core_src/viewport.cpp +36 -12
  40. package/cpp/_core_src/viewport.h +50 -0
  41. package/cpp/_core_src/volume.cpp +33 -8
  42. package/cpp/_core_src/volume.h +12 -1
  43. package/cpp/_core_src/volume_anim.cpp +32 -0
  44. package/cpp/_core_src/volume_anim.h +41 -0
  45. package/lib/index.d.mts +261 -25
  46. package/lib/index.d.ts +261 -25
  47. package/lib/index.js +252 -36
  48. package/lib/index.js.map +1 -1
  49. package/lib/index.mjs +252 -36
  50. package/lib/index.mjs.map +1 -1
  51. package/package.json +5 -4
  52. package/src/VroomChart.tsx +162 -11
  53. package/src/easing.ts +40 -0
  54. package/src/index.ts +5 -0
  55. package/src/jsi.d.ts +124 -20
  56. package/src/theme.ts +1 -0
  57. package/src/types.ts +5 -0
  58. package/src/useChartCore.ts +166 -28
@@ -20,17 +20,29 @@
20
20
 
21
21
  #include "chart.h"
22
22
  #include "fonts.h"
23
+ #include "style_inherit.h"
23
24
  #include "theme.h"
24
25
  #include "viewport.h"
25
26
 
26
27
  namespace vroom::macd_pane {
27
28
 
28
29
  namespace {
30
+ using vroom::style::color_or;
31
+ using vroom::style::width_or;
32
+
29
33
  constexpr SkColor kMacdLine = 0xff2962ff; // blue MACD line
30
34
  constexpr SkColor kSignalLine = 0xffff6d00; // orange signal line
31
35
  constexpr SkColor kZeroLine = 0xff484f58; // zero reference
32
36
  constexpr SkColor kDivider = 0xff21262d; // pane separator
33
37
  constexpr float kPadFrac = 0.85f; // keep curves off the band edges
38
+ constexpr float kLineWidth = 1.5f; // default stroke for both lines
39
+ constexpr float kFadedAlpha = 0.5f; // easing histogram bars
40
+
41
+ // The easing histogram bars default to their own base color dimmed, which is
42
+ // how the pane read before the colors were configurable.
43
+ SkColor faded_or(uint32_t configured, SkColor base) {
44
+ return vroom::style::faded_or(configured, base, kFadedAlpha);
45
+ }
34
46
  } // namespace
35
47
 
36
48
  void draw(SkCanvas* canvas,
@@ -51,23 +63,25 @@ void draw(SkCanvas* canvas,
51
63
  const float band_h = pane_bottom - pane_top;
52
64
  if (band_h <= 0.f) return;
53
65
 
66
+ const VroomMACD& cfg = chart.macd;
54
67
  const float mid = (pane_top + pane_bottom) * 0.5f;
55
68
  // User y-zoom scales the amplitude about the zero line (mid). 1.0 = the
56
69
  // default auto-fit; >1 zooms in, <1 zooms out.
57
70
  const float half =
58
71
  band_h * 0.5f * kPadFrac * static_cast<float>(chart.macd_y_scale);
59
72
 
60
- // Auto-scale symmetric about zero across all visible finite values.
73
+ // Auto-scale symmetric about zero across the finite values on show, so
74
+ // hiding a series lets the rest fill the pane.
61
75
  double scale = 0.0;
62
- auto track = [&](const double* s) {
63
- if (!s) return;
76
+ auto track = [&](const double* s, bool shown) {
77
+ if (!s || !shown) return;
64
78
  for (std::size_t i = 0; i < n; ++i) {
65
79
  if (std::isfinite(s[i])) scale = std::max(scale, std::abs(s[i]));
66
80
  }
67
81
  };
68
- track(macd_visible);
69
- track(signal_visible);
70
- track(hist_visible);
82
+ track(macd_visible, cfg.line_visible != 0);
83
+ track(signal_visible, cfg.signal_visible != 0);
84
+ track(hist_visible, cfg.hist_visible != 0);
71
85
 
72
86
  auto y_for = [&](double v) -> float {
73
87
  if (scale <= 0.0) return mid;
@@ -90,34 +104,44 @@ void draw(SkCanvas* canvas,
90
104
  canvas->clipRect(SkRect::MakeLTRB(0.f, pane_top, candle_right, pane_bottom));
91
105
 
92
106
  // Zero line.
93
- SkPaint zero;
94
- zero.setAntiAlias(true);
95
- zero.setColor(kZeroLine);
96
- zero.setStrokeWidth(1.f);
97
- canvas->drawLine(0.f, mid, candle_right, mid, zero);
98
-
99
- // Histogram bars: from the zero line to y_for(hist), 4-color (green above /
100
- // red below, full alpha when accelerating, half when decelerating).
101
- if (hist_visible) {
107
+ if (cfg.zero_visible) {
108
+ SkPaint zero;
109
+ zero.setAntiAlias(true);
110
+ zero.setColor(color_or(cfg.zero_color, kZeroLine));
111
+ zero.setStrokeWidth(1.f);
112
+ canvas->drawLine(0.f, mid, candle_right, mid, zero);
113
+ }
114
+
115
+ // Histogram bars: from the zero line to y_for(hist), 4-color — above or
116
+ // below zero, each in a building and an easing shade. A bar is building
117
+ // while it grows away from zero and easing while it falls back toward it.
118
+ if (hist_visible && cfg.hist_visible) {
102
119
  const float body_w =
103
120
  vroom::candle_body_width(lay, window_ms, candle_duration_ms);
104
121
  const float half_body = body_w * 0.5f;
105
122
  const float zero_y = y_for(0.0);
106
- const SkColor bull = chart.theme.colors[VROOM_COLOR_ACCENT_BULL];
107
- const SkColor bear = chart.theme.colors[VROOM_COLOR_ACCENT_BEAR];
123
+ const SkColor up =
124
+ color_or(cfg.hist_up_color, chart.theme.colors[VROOM_COLOR_ACCENT_BULL]);
125
+ const SkColor down =
126
+ color_or(cfg.hist_down_color, chart.theme.colors[VROOM_COLOR_ACCENT_BEAR]);
127
+ const SkColor up_easing = faded_or(cfg.hist_up_fading_color, up);
128
+ const SkColor down_easing = faded_or(cfg.hist_down_fading_color, down);
108
129
  for (std::size_t i = 0; i < n; ++i) {
109
130
  const double h = hist_visible[i];
110
131
  if (!std::isfinite(h)) continue;
111
132
  const bool have_prev = i > 0 && std::isfinite(hist_visible[i - 1]);
112
133
  const double prev = have_prev ? hist_visible[i - 1] : h;
113
- bool accelerating = true;
134
+ bool building = true;
114
135
  if (have_prev) {
115
- accelerating = h >= 0.0 ? h >= prev : h <= prev;
136
+ building = h >= 0.0 ? h >= prev : h <= prev;
116
137
  }
117
138
  SkPaint bar;
118
139
  bar.setAntiAlias(true);
119
- bar.setColor(h >= 0.0 ? bull : bear);
120
- bar.setAlphaf(accelerating ? 1.0f : 0.5f);
140
+ if (h >= 0.0) {
141
+ bar.setColor(building ? up : up_easing);
142
+ } else {
143
+ bar.setColor(building ? down : down_easing);
144
+ }
121
145
  const float cx = vroom::candle_center_x(
122
146
  lay, visible[i].time_ms, candle_duration_ms, visible_start_ms,
123
147
  window_ms);
@@ -130,7 +154,7 @@ void draw(SkCanvas* canvas,
130
154
  }
131
155
 
132
156
  // MACD + signal lines.
133
- auto stroke_series = [&](const double* series, SkColor color) {
157
+ auto stroke_series = [&](const double* series, SkColor color, float width) {
134
158
  if (!series) return;
135
159
  SkPathBuilder path;
136
160
  bool pen_down = false;
@@ -155,11 +179,17 @@ void draw(SkCanvas* canvas,
155
179
  line.setAntiAlias(true);
156
180
  line.setColor(color);
157
181
  line.setStyle(SkPaint::kStroke_Style);
158
- line.setStrokeWidth(1.5f);
182
+ line.setStrokeWidth(width);
159
183
  canvas->drawPath(path.detach(), line);
160
184
  };
161
- stroke_series(signal_visible, kSignalLine);
162
- stroke_series(macd_visible, kMacdLine); // MACD over signal
185
+ if (cfg.signal_visible) {
186
+ stroke_series(signal_visible, color_or(cfg.signal_color, kSignalLine),
187
+ width_or(cfg.signal_width, kLineWidth));
188
+ }
189
+ if (cfg.line_visible) { // MACD over signal
190
+ stroke_series(macd_visible, color_or(cfg.line_color, kMacdLine),
191
+ width_or(cfg.line_width, kLineWidth));
192
+ }
163
193
 
164
194
  // Caption, top-left of the pane.
165
195
  auto tf = vroom::axis_typeface();
@@ -168,8 +198,8 @@ void draw(SkCanvas* canvas,
168
198
  font.setSubpixel(true);
169
199
  font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
170
200
  char caption[40];
171
- std::snprintf(caption, sizeof(caption), "MACD %d %d %d", chart.macd_fast,
172
- chart.macd_slow, chart.macd_signal);
201
+ std::snprintf(caption, sizeof(caption), "MACD %d %d %d", cfg.fast,
202
+ cfg.slow, cfg.signal);
173
203
  SkRect cb;
174
204
  font.measureText(caption, std::strlen(caption), SkTextEncoding::kUTF8,
175
205
  &cb);
@@ -0,0 +1,87 @@
1
+ #include "price_line_layout.h"
2
+
3
+ #include <algorithm>
4
+ #include <cmath>
5
+
6
+ namespace vroom::price_lines {
7
+
8
+ namespace {
9
+ // A pill's width is its text plus symmetric padding. Zero-width text means the
10
+ // segment is absent, not a padding-only sliver.
11
+ float pill_width(float text_w) {
12
+ return text_w > 0.f ? text_w + 2.f * kPadH : 0.f;
13
+ }
14
+ } // namespace
15
+
16
+ bool contains(const Rect& r, float x, float y) {
17
+ if (r.empty()) return false;
18
+ return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
19
+ }
20
+
21
+ GroupLayout layout_group(const LabelMetrics& metrics,
22
+ float y,
23
+ float pane_right,
24
+ const ::VroomPriceLineStyle& style) {
25
+ GroupLayout out;
26
+
27
+ const float body_w = pill_width(metrics.text_w);
28
+ const float qty_w = pill_width(metrics.quantity_w);
29
+ // The close button is a square cell, so its icon stays centered whatever the
30
+ // font size works out to.
31
+ const float close_w = metrics.closable ? metrics.label_h : 0.f;
32
+ const float group_w = body_w + qty_w + close_w;
33
+
34
+ if (group_w <= 0.f || metrics.label_h <= 0.f || pane_right <= 0.f) {
35
+ // A bare line: no group, but still report a degenerate span at the
36
+ // anchor so callers can treat left/right uniformly.
37
+ out.left = out.right = pane_right;
38
+ return out;
39
+ }
40
+
41
+ float left = 0.f;
42
+ switch (style.align) {
43
+ case 0: // left
44
+ left = 0.f;
45
+ break;
46
+ case 1: // center
47
+ left = (pane_right - group_w) * 0.5f;
48
+ break;
49
+ default: { // right
50
+ const float frac = std::clamp(style.line_length_frac, 0.f, 1.f);
51
+ const float right = pane_right - frac * pane_right - kAxisGutter;
52
+ left = right - group_w;
53
+ break;
54
+ }
55
+ }
56
+ // Keep the group on-screen: never past the axis, never off the left edge.
57
+ left = std::min(left, pane_right - group_w);
58
+ left = std::max(left, 0.f);
59
+
60
+ const float top = y - metrics.label_h * 0.5f;
61
+ const float bottom = top + metrics.label_h;
62
+
63
+ float x = left;
64
+ if (body_w > 0.f) {
65
+ out.body = Rect{x, top, x + body_w, bottom};
66
+ x += body_w;
67
+ }
68
+ if (qty_w > 0.f) {
69
+ out.quantity = Rect{x, top, x + qty_w, bottom};
70
+ x += qty_w;
71
+ }
72
+ if (close_w > 0.f) {
73
+ out.close = Rect{x, top, x + close_w, bottom};
74
+ x += close_w;
75
+ }
76
+
77
+ out.left = left;
78
+ out.right = x;
79
+ return out;
80
+ }
81
+
82
+ bool hits_line(float line_y, float stroke_width, float y) {
83
+ const float band = std::max(stroke_width, 1.f) + kLineHitTolerance;
84
+ return std::fabs(y - line_y) <= band;
85
+ }
86
+
87
+ } // namespace vroom::price_lines
@@ -0,0 +1,80 @@
1
+ // Price-status-line geometry — pure functions, no Skia, no state.
2
+ //
3
+ // Split out from price_lines.cpp so the rendering pass and the hit-test pass
4
+ // derive their rectangles from the same arithmetic: a label the user can see but
5
+ // not click (or vice versa) is the classic failure mode for this widget. Being
6
+ // Skia-free it also unit-tests without a Skia checkout.
7
+
8
+ #pragma once
9
+
10
+ #include "vroom/vroom_chart.h"
11
+ namespace vroom::price_lines {
12
+
13
+ // Pill padding / corner radius, matching the current-price indicator so a price
14
+ // line and the price badge read as the same family of chrome.
15
+ constexpr float kPadH = 8.f; // left/right of the text
16
+ constexpr float kPadV = 4.f; // above/below the text
17
+ constexpr float kCorner = 6.f; // rounded-pill corner radius
18
+
19
+ // Extra grab band above and below the stroke, so a 1px line is still an easy
20
+ // target. Matches the tolerance mainstream chart libraries settled on.
21
+ constexpr float kLineHitTolerance = 7.f;
22
+
23
+ // Gap between the label group and the price axis when right-aligned. Wide enough
24
+ // that the line still shows through it — a couple of dashes reading as "this
25
+ // continues to the badge" rather than one orphaned dot.
26
+ constexpr float kAxisGutter = 12.f;
27
+
28
+ struct Rect {
29
+ float left = 0.f;
30
+ float top = 0.f;
31
+ float right = 0.f;
32
+ float bottom = 0.f;
33
+
34
+ // Empty rects stand in for absent segments (no quantity, no close button).
35
+ bool empty() const { return right <= left || bottom <= top; }
36
+ float width() const { return right - left; }
37
+ };
38
+
39
+ // True when (x, y) falls inside `r`. Always false for an empty rect.
40
+ bool contains(const Rect& r, float x, float y);
41
+
42
+ // The text extents a caller measured with the real font, plus which optional
43
+ // segments to reserve room for. `label_h` is the pill height (already including
44
+ // kPadV on both sides).
45
+ struct LabelMetrics {
46
+ float text_w = 0.f; // body text width; 0 hides the body pill
47
+ float quantity_w = 0.f; // quantity text width; 0 hides the quantity pill
48
+ float label_h = 0.f;
49
+ bool closable = false; // reserve the trailing square close-button cell
50
+ };
51
+
52
+ // The laid-out label group, vertically centered on the line. Any of the three
53
+ // segment rects may be empty. `left`/`right` bound whichever segments exist and
54
+ // are equal when the group is empty (a bare line).
55
+ struct GroupLayout {
56
+ Rect body;
57
+ Rect quantity;
58
+ Rect close;
59
+ float left = 0.f;
60
+ float right = 0.f;
61
+
62
+ bool empty() const { return right <= left; }
63
+ };
64
+
65
+ // Lays the group out on the line at `y`. `pane_right` is the inner edge of the
66
+ // price axis (the line's right end).
67
+ //
68
+ // `style.align` picks the anchor: 2 (right, the default) parks the group's right
69
+ // edge `style.line_length_frac` of the pane width in from the axis; 1 centers
70
+ // it; 0 pins it to the left edge. The group is clamped into [0, pane_right], so
71
+ // a label wider than the pane starts flush left rather than overflowing.
72
+ GroupLayout layout_group(const LabelMetrics& metrics,
73
+ float y,
74
+ float pane_right,
75
+ const ::VroomPriceLineStyle& style);
76
+
77
+ // True when `y` is within the grab band of a line drawn at `line_y`.
78
+ bool hits_line(float line_y, float stroke_width, float y);
79
+
80
+ } // namespace vroom::price_lines