react-native-vroom-chart 0.10.0 → 0.11.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.
@@ -36,6 +36,7 @@
36
36
  #include "rsi.h"
37
37
  #include "rsi_pane.h"
38
38
  #include "style_inherit.h"
39
+ #include "tip_anchor.h"
39
40
  #include "tip_pulse.h"
40
41
  #include "volume.h"
41
42
  #include "vwap.h"
@@ -311,16 +312,23 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
311
312
  // 5.8. Line-mode tip marker — the dot (and optional pulse) at the newest
312
313
  // close. Above the overlays so it stays the eye's anchor, but before the
313
314
  // axis masks, which trim the ring at the price scale.
315
+ //
316
+ // Unlike every layer above, this one anchors to the newest candle in
317
+ // the series rather than the newest on screen, so panning into history
318
+ // carries the dot off the right edge with its candle (tip_anchor.h).
314
319
  if (fade > 0.f && theme.floats[VROOM_FLOAT_LINE_TIP_DOT] > 0.5f) {
320
+ const auto anchor = vroom::tip_anchor::at(range.start, range.end,
321
+ candles.size(), morphing);
315
322
  vroom::ma_overlay::draw_close_tip(
316
- canvas, lay, bounds, visible, n, window_ms, visible_start_ms,
317
- candle_duration_ms, candle_right, candle_area_h,
323
+ canvas, lay, bounds, visible, anchor.slot_count, window_ms,
324
+ visible_start_ms, candle_duration_ms, candle_right, candle_area_h,
318
325
  theme.colors[VROOM_COLOR_LINE],
319
326
  theme.colors[VROOM_COLOR_BACKGROUND],
320
327
  theme.floats[VROOM_FLOAT_LINE_WIDTH_PX], fade,
321
328
  theme.floats[VROOM_FLOAT_LINE_TIP_PULSE] > 0.5f,
322
329
  tip_pulse_elapsed_s / vroom::tip_pulse::kPeriodSeconds,
323
- morph_src, morph_n, morph_t);
330
+ anchor.use_morph ? morph_src : nullptr,
331
+ anchor.use_morph ? morph_n : 0, morph_t);
324
332
  }
325
333
 
326
334
  // 6. Axis backgrounds (mask any candle overflow). The x-axis separator
@@ -23,6 +23,7 @@
23
23
  #pragma clang diagnostic pop
24
24
 
25
25
  #include "labels.h"
26
+ #include "price_format.h"
26
27
  #include "theme.h"
27
28
  #include "viewport.h"
28
29
 
@@ -89,6 +90,12 @@ struct VroomChart {
89
90
  // label. 0 = uncomputed; layout() falls back to a width ratio.
90
91
  float axis_width_px = 0.f;
91
92
 
93
+ // How prices render, derived from the asset's own scale. Cached beside the
94
+ // width because the two are measured together — every label site reads this
95
+ // so none of them can disagree with the strip they're drawn in. Both are
96
+ // refreshed by labels::recompute_axis_width.
97
+ vroom::PriceFormat price_fmt{};
98
+
92
99
  // --- price bounds (y-axis state) ---------------------------------------
93
100
  // Two modes. Auto (price_bounds_manual == false, the default): the y-range
94
101
  // continuously follows the visible candles each frame and `price_bounds`
@@ -20,6 +20,7 @@
20
20
 
21
21
  #include "chart.h"
22
22
  #include "fonts.h"
23
+ #include "price_format.h"
23
24
  #include "theme.h"
24
25
  #include "ticks.h"
25
26
 
@@ -146,11 +147,12 @@ void draw(SkCanvas* canvas,
146
147
  // sharing the y-axis labels' column.
147
148
  if (lay.y_axis_width_px > 0.f) {
148
149
  const double price = vroom::y_to_price(lay, bounds, cy);
149
- char buf[32];
150
- const int decimals = vroom::price_decimals(
150
+ char buf[48];
151
+ const vroom::PriceFormat fmt = vroom::with_tick_guard(
152
+ chart.price_fmt,
151
153
  vroom::pick_price_interval(bounds.max - bounds.min,
152
154
  vroom::price_pane_bottom(lay)));
153
- vroom::format_price(buf, sizeof(buf), price, decimals);
155
+ vroom::format_price(buf, sizeof(buf), price, fmt);
154
156
  const float axis_center_x = lay.width_px - lay.y_axis_width_px * 0.5f;
155
157
  draw_badge(canvas, font, buf, axis_center_x, cy, badge_fill,
156
158
  SK_ColorWHITE);
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include "chart.h"
21
21
  #include "fonts.h"
22
+ #include "price_format.h"
22
23
  #include "ticks.h"
23
24
  #include "viewport.h"
24
25
 
@@ -47,6 +48,19 @@ void advance(Fade& f, float step, float dt) {
47
48
  }
48
49
  }
49
50
 
51
+ // What the asset is worth, which is what its price precision keys off. The
52
+ // newest close rather than the visible range, so panning and zooming don't
53
+ // shift the decimals around under the user.
54
+ //
55
+ // An asset that has crossed orders of magnitude within one series (a token at
56
+ // 0.00001 that later trades at 100) reads its recent scale here, so deep
57
+ // history would round flat — the tick guard at each label site is what rescues
58
+ // it, since the bounds down there are tiny enough to demand more decimals.
59
+ double reference_price(const VroomChart& chart) {
60
+ if (!chart.candles.empty()) return chart.candles.back().close;
61
+ return (chart.price_bounds.max + chart.price_bounds.min) * 0.5;
62
+ }
63
+
50
64
  // Drives a whole axis off the morph envelope instead of the per-label fades:
51
65
  // everything in the phase's tick set shares one opacity, and anything outside it
52
66
  // is dropped outright. At the midpoint the axis is transparent, so swapping the
@@ -153,15 +167,16 @@ void draw_y_labels(SkCanvas* canvas,
153
167
  // Horizontal center of the y-axis container ([width - y_axis_width, width]).
154
168
  // Labels (and the price box) center on this so their text shares a column.
155
169
  const float axis_center_x = lay.width_px - lay.y_axis_width_px * 0.5f;
156
- const int decimals = vroom::price_decimals(
170
+ const vroom::PriceFormat fmt = vroom::with_tick_guard(
171
+ chart.price_fmt,
157
172
  vroom::pick_price_interval(bounds.max - bounds.min, candle_area_h));
158
173
 
159
174
  for (const auto& f : chart.y_fades) {
160
175
  if (f.opacity <= 1e-3f) continue;
161
176
  const float y = vroom::price_to_y(lay, bounds, f.price);
162
177
 
163
- char buf[32];
164
- vroom::format_price(buf, sizeof(buf), f.price, decimals);
178
+ char buf[48];
179
+ vroom::format_price(buf, sizeof(buf), f.price, fmt);
165
180
  const size_t len = std::strlen(buf);
166
181
  const float text_w = font.measureText(
167
182
  buf, len, SkTextEncoding::kUTF8);
@@ -347,6 +362,10 @@ void gc_x_fades(VroomChart& chart) {
347
362
  // ----- Axis-width sizing ----------------------------------------------------
348
363
 
349
364
  void recompute_axis_width(VroomChart& chart) {
365
+ // Precision first, and unconditionally: the label sites read it whether or
366
+ // not a typeface has loaded, and it's what the measurement below sizes for.
367
+ chart.price_fmt = vroom::price_format_for(reference_price(chart));
368
+
350
369
  auto tf = vroom::axis_typeface();
351
370
  if (!tf) {
352
371
  chart.axis_width_px = 0.f;
@@ -370,11 +389,16 @@ void recompute_axis_width(VroomChart& chart) {
370
389
 
371
390
  SkFont font(tf, chart.theme.floats[VROOM_FLOAT_AXIS_FONT_SIZE_PX]);
372
391
  const auto lay = chart.layout();
373
- const int decimals = vroom::price_decimals(
392
+ // Same guard the label sites apply, against the bounds this is sizing for,
393
+ // so a zoom deep enough to add decimals widens the strip to hold them.
394
+ const vroom::PriceFormat fmt = vroom::with_tick_guard(
395
+ chart.price_fmt,
374
396
  vroom::pick_price_interval(hi - lo, vroom::price_pane_bottom(lay)));
375
- char buf_hi[32], buf_lo[32];
376
- vroom::format_price(buf_hi, sizeof(buf_hi), hi, decimals);
377
- vroom::format_price(buf_lo, sizeof(buf_lo), lo, decimals);
397
+ // At the same decimals the longest label is whichever bound has the most
398
+ // integer digits, separators included.
399
+ char buf_hi[48], buf_lo[48];
400
+ vroom::format_price(buf_hi, sizeof(buf_hi), hi, fmt);
401
+ vroom::format_price(buf_lo, sizeof(buf_lo), lo, fmt);
378
402
  const float w_hi = font.measureText(
379
403
  buf_hi, std::strlen(buf_hi), SkTextEncoding::kUTF8);
380
404
  const float w_lo = font.measureText(
@@ -108,6 +108,11 @@ void draw_close_gradient(SkCanvas* canvas,
108
108
  // switch. `opacity` scales everything, fading the marker in with the line during
109
109
  // the candle→line morph.
110
110
  //
111
+ // `n` counts the slots to the candle being marked, which is the series' newest
112
+ // rather than the visible slice's — see tip_anchor.h, which derives it. That
113
+ // candle can sit off the pane when panned into history, and the marker then
114
+ // draws nothing.
115
+ //
111
116
  // `pulse_phase` is in cycles and wraps, so the caller can hand over elapsed time
112
117
  // divided by tip_pulse::kPeriodSeconds. Ignored unless `pulse`.
113
118
  void draw_close_tip(SkCanvas* canvas,
@@ -0,0 +1,87 @@
1
+ #include "price_format.h"
2
+
3
+ #include <algorithm>
4
+ #include <cmath>
5
+ #include <cstdio>
6
+
7
+ namespace vroom {
8
+
9
+ namespace {
10
+
11
+ // Room for %.*f of any finite double at kPriceMaxDecimals: sign, the 309
12
+ // integer digits DBL_MAX can reach, the point, and the decimals.
13
+ constexpr size_t kPlainCap = 340;
14
+
15
+ constexpr char kGroupSeparator = ',';
16
+ constexpr int kGroupSize = 3;
17
+
18
+ } // namespace
19
+
20
+ int significant_decimals(double reference) {
21
+ const double r = std::fabs(reference);
22
+ if (!(r > 0.0) || !std::isfinite(r)) return kPriceMinDecimals;
23
+ // floor(log10) is the exponent of the leading digit, so subtracting it from
24
+ // the digit budget lands the last significant digit on the final decimal.
25
+ const int exponent = static_cast<int>(std::floor(std::log10(r)));
26
+ return std::clamp(kPriceSigDigits - 1 - exponent, kPriceMinDecimals,
27
+ kPriceMaxDecimals);
28
+ }
29
+
30
+ PriceFormat price_format_for(double reference) {
31
+ return PriceFormat{significant_decimals(reference), true};
32
+ }
33
+
34
+ int price_decimals(double interval) {
35
+ if (!(interval > 0.0) || !std::isfinite(interval)) return kPriceMinDecimals;
36
+ const double d = std::ceil(-std::log10(interval) - 1e-12);
37
+ if (d < 0.0) return 0;
38
+ if (d > static_cast<double>(kPriceMaxDecimals)) return kPriceMaxDecimals;
39
+ return static_cast<int>(d);
40
+ }
41
+
42
+ PriceFormat with_tick_guard(const PriceFormat& fmt, double interval) {
43
+ return PriceFormat{std::max(fmt.decimals, price_decimals(interval)),
44
+ fmt.group};
45
+ }
46
+
47
+ void format_price(char* buf, size_t buf_size, double price,
48
+ const PriceFormat& fmt) {
49
+ if (!buf || buf_size == 0) return;
50
+ const int decimals = std::clamp(fmt.decimals, 0, kPriceMaxDecimals);
51
+
52
+ char plain[kPlainCap];
53
+ if (std::snprintf(plain, sizeof(plain), "%.*f", decimals, price) < 0) {
54
+ buf[0] = '\0';
55
+ return;
56
+ }
57
+ if (!fmt.group) {
58
+ std::snprintf(buf, buf_size, "%s", plain);
59
+ return;
60
+ }
61
+
62
+ const char* src = plain;
63
+ size_t out = 0;
64
+ // Leaves room for the terminator on every write, so the result truncates
65
+ // the way snprintf would rather than running off the end.
66
+ const auto put = [&](char c) {
67
+ if (out + 1 < buf_size) buf[out++] = c;
68
+ };
69
+
70
+ if (*src == '-' || *src == '+') put(*src++);
71
+
72
+ // Digits up to the point, or the whole run when decimals == 0. A non-finite
73
+ // price has none, and falls through to the tail copy as "inf" / "nan".
74
+ const char* int_end = src;
75
+ while (*int_end >= '0' && *int_end <= '9') ++int_end;
76
+
77
+ const ptrdiff_t digits = int_end - src;
78
+ for (ptrdiff_t i = 0; i < digits; ++i) {
79
+ if (i > 0 && (digits - i) % kGroupSize == 0) put(kGroupSeparator);
80
+ put(src[i]);
81
+ }
82
+ for (const char* p = int_end; *p != '\0'; ++p) put(*p);
83
+
84
+ buf[out] = '\0';
85
+ }
86
+
87
+ } // namespace vroom
@@ -0,0 +1,69 @@
1
+ // How a price is turned into the string on the y-axis.
2
+ //
3
+ // Precision follows the asset's own scale rather than the tick interval: a
4
+ // sub-cent token needs nine decimals to say anything at all, while a
5
+ // five-figure one is unreadable past two. Deriving it from the interval — as
6
+ // the axis used to — gets both wrong, printing "84000" for the one and
7
+ // "0.000060" for the other.
8
+ //
9
+ // The rule is a fixed count of significant digits with a floor, which is what
10
+ // reproduces a conventional price scale across the whole range:
11
+ //
12
+ // 4.4094e-5 -> 9 decimals 0.000044094
13
+ // 0.023397 -> 6 decimals 0.023397
14
+ // 80285.20 -> 2 decimals 80,285.20 (floor)
15
+ // 2513.92 -> 2 decimals 2,513.92 (floor)
16
+ //
17
+ // The reference is the asset's own price, not the visible range, so the
18
+ // precision holds steady while the user pans and zooms. A range-derived count
19
+ // would change under the user's finger and drag the axis width along with it.
20
+ //
21
+ // Skia-free and separately compiled so the unit tests can cover it; see
22
+ // tests/test_price_format.cpp.
23
+
24
+ #pragma once
25
+
26
+ #include <cstddef>
27
+
28
+ namespace vroom {
29
+
30
+ // Significant digits a price is quoted to. Five is what conventional price
31
+ // scales show: it keeps 0.023397 intact and still fits 0.000044094.
32
+ inline constexpr int kPriceSigDigits = 5;
33
+
34
+ // Prices at or above ~1 unit read as currency, where fewer than two decimals
35
+ // looks broken (a "$84,000" axis next to a "$80,285.20" last price).
36
+ inline constexpr int kPriceMinDecimals = 2;
37
+
38
+ // Past this the digits are noise, and %.*f starts printing the binary
39
+ // representation's tail rather than anything the feed meant.
40
+ inline constexpr int kPriceMaxDecimals = 12;
41
+
42
+ // How to render a price. `group` inserts thousands separators, which only ever
43
+ // affects the integer part.
44
+ struct PriceFormat {
45
+ int decimals = kPriceMinDecimals;
46
+ bool group = true;
47
+ };
48
+
49
+ // Decimals giving kPriceSigDigits significant digits for an asset priced around
50
+ // |reference|, clamped to [kPriceMinDecimals, kPriceMaxDecimals]. A zero or
51
+ // non-finite reference falls back to the floor.
52
+ int significant_decimals(double reference);
53
+
54
+ // The format for an asset priced around `reference`.
55
+ PriceFormat price_format_for(double reference);
56
+
57
+ // Decimal places needed so adjacent ticks `interval` apart don't collapse to
58
+ // the same string. 0.01 → 2, 0.005 → 3, 1e-8 → 8. Clamped to [0, 12].
59
+ int price_decimals(double interval);
60
+
61
+ // `fmt` raised, if need be, so ticks `interval` apart stay distinct. The asset's
62
+ // precision is the floor; only a zoom deeper than that precision adds to it.
63
+ PriceFormat with_tick_guard(const PriceFormat& fmt, double interval);
64
+
65
+ // Writes `price` into `buf` per `fmt`, truncating rather than overrunning.
66
+ void format_price(char* buf, size_t buf_size, double price,
67
+ const PriceFormat& fmt);
68
+
69
+ } // namespace vroom
@@ -18,6 +18,7 @@
18
18
 
19
19
  #include "chart.h"
20
20
  #include "fonts.h"
21
+ #include "price_format.h"
21
22
  #include "theme.h"
22
23
  #include "ticks.h"
23
24
  #include "viewport.h"
@@ -66,10 +67,11 @@ void draw(SkCanvas* canvas,
66
67
  font.setSubpixel(true);
67
68
  font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
68
69
 
69
- char buf[32];
70
- const int decimals = vroom::price_decimals(
70
+ char buf[48];
71
+ const vroom::PriceFormat fmt = vroom::with_tick_guard(
72
+ chart.price_fmt,
71
73
  vroom::pick_price_interval(bounds.max - bounds.min, candle_area_h));
72
- vroom::format_price(buf, sizeof(buf), last.close, decimals);
74
+ vroom::format_price(buf, sizeof(buf), last.close, fmt);
73
75
  const size_t len = std::strlen(buf);
74
76
 
75
77
  // Measure the tight glyph bounds (origin at the baseline) so we can center
@@ -21,6 +21,7 @@
21
21
 
22
22
  #include "chart.h"
23
23
  #include "fonts.h"
24
+ #include "price_format.h"
24
25
  #include "price_line_layout.h"
25
26
  #include "theme.h"
26
27
  #include "ticks.h"
@@ -214,11 +215,11 @@ void draw_axis_badge(SkCanvas* canvas,
214
215
  double price,
215
216
  float y,
216
217
  SkColor fill,
217
- int decimals) {
218
+ const PriceFormat& fmt) {
218
219
  if (lay.y_axis_width_px <= 0.f) return;
219
220
 
220
- char buf[32];
221
- vroom::format_price(buf, sizeof(buf), price, decimals);
221
+ char buf[48];
222
+ vroom::format_price(buf, sizeof(buf), price, fmt);
222
223
  const size_t len = std::strlen(buf);
223
224
 
224
225
  SkRect tb;
@@ -255,7 +256,8 @@ void draw(SkCanvas* canvas,
255
256
  const VroomPriceLineStyle& style = chart.price_line_style;
256
257
  SkFont font;
257
258
  const bool has_font = label_font(chart, &font);
258
- const int decimals = vroom::price_decimals(
259
+ const vroom::PriceFormat fmt = vroom::with_tick_guard(
260
+ chart.price_fmt,
259
261
  vroom::pick_price_interval(bounds.max - bounds.min, candle_area_h));
260
262
 
261
263
  for (size_t i = 0; i < chart.price_lines.size(); ++i) {
@@ -355,7 +357,7 @@ void draw(SkCanvas* canvas,
355
357
 
356
358
  if (has_font && (pl.flags & VROOM_PRICE_LINE_AXIS_LABEL) != 0) {
357
359
  draw_axis_badge(canvas, font, lay, render_price(chart, i), y,
358
- line_color, decimals);
360
+ line_color, fmt);
359
361
  }
360
362
  }
361
363
  }
@@ -2,7 +2,6 @@
2
2
 
3
3
  #include <algorithm>
4
4
  #include <cmath>
5
- #include <cstdio>
6
5
  #include <ctime>
7
6
 
8
7
  namespace vroom {
@@ -146,17 +145,4 @@ double pick_price_interval(double range, float candle_area_h) {
146
145
  return nice * magnitude;
147
146
  }
148
147
 
149
- int price_decimals(double interval) {
150
- if (!(interval > 0.0) || !std::isfinite(interval)) return 2;
151
- const double d = std::ceil(-std::log10(interval) - 1e-12);
152
- if (d < 0.0) return 0;
153
- if (d > 12.0) return 12;
154
- return static_cast<int>(d);
155
- }
156
-
157
- void format_price(char* buf, size_t buf_size, double price, int decimals) {
158
- if (!buf || buf_size == 0) return;
159
- std::snprintf(buf, buf_size, "%.*f", decimals, price);
160
- }
161
-
162
148
  } // namespace vroom
@@ -6,7 +6,6 @@
6
6
 
7
7
  #pragma once
8
8
 
9
- #include <cstddef>
10
9
  #include <cstdint>
11
10
 
12
11
  namespace vroom {
@@ -49,13 +48,8 @@ int64_t next_tick(int64_t t, const TimeTick& tick);
49
48
  // "Nice number" tick selection for the y-axis: snaps to 1, 2, or 5 × 10ⁿ
50
49
  // based on the price range and target spacing. Returns the price interval
51
50
  // in the same units as the data (e.g. dollars).
51
+ //
52
+ // Turning that interval into a label string is price_format.h's job.
52
53
  double pick_price_interval(double range, float candle_area_h);
53
54
 
54
- // Decimal places needed so adjacent ticks `interval` apart don't collapse to
55
- // the same string. 0.01 → 2, 0.005 → 3, 1e-8 → 8. Clamped to [0, 12].
56
- int price_decimals(double interval);
57
-
58
- // Writes `price` into `buf` using `decimals` (from price_decimals).
59
- void format_price(char* buf, size_t buf_size, double price, int decimals);
60
-
61
55
  } // namespace vroom
@@ -0,0 +1,49 @@
1
+ // Which slot the line chart's tip marker anchors to (see draw_close_tip in
2
+ // ma_overlay.cpp).
3
+ //
4
+ // The tip marks the newest close in the *series*, not the newest close on
5
+ // screen — the same rule the price indicator follows (price_indicator.cpp).
6
+ // Panning into history therefore carries the dot off the right edge with the
7
+ // candle it belongs to, where draw_close_tip's on-pane guard drops it.
8
+ //
9
+ // Every other line-chart layer draws the visible slice, so the tip is the one
10
+ // caller that has to reach past range.end. The slice is a view into the same
11
+ // contiguous candle buffer, so widening the slot count is enough to reach the
12
+ // newest candle — no second pointer.
13
+ //
14
+ // Skia-free and header-only so the unit tests can cover it; see
15
+ // tests/test_tip_anchor.cpp.
16
+
17
+ #pragma once
18
+
19
+ #include <cstddef>
20
+
21
+ namespace vroom::tip_anchor {
22
+
23
+ // How draw_close_tip should be called for one frame.
24
+ struct Anchor {
25
+ // Slot count to pass in place of the visible count, so slot 0 resolves to
26
+ // the newest candle in the series. Never 0 for a non-empty visible slice.
27
+ std::size_t slot_count;
28
+ // Whether the interval-morph capture still lines up with slot 0.
29
+ bool use_morph;
30
+ };
31
+
32
+ // `range_start`/`range_end` are the visible slice's half-open bounds into a
33
+ // series of `total` candles; `morphing` is whether an interval morph is running.
34
+ //
35
+ // The capture taken at a timeframe switch is indexed from the newest *visible*
36
+ // candle, so it only pairs with slot 0 while the newest candle is the one at the
37
+ // right edge. Scrolled back, pairing them would interpolate the tip between two
38
+ // unrelated candles and strand the dot mid-pane for the length of the switch.
39
+ inline Anchor at(std::size_t range_start,
40
+ std::size_t range_end,
41
+ std::size_t total,
42
+ bool morphing) {
43
+ // A caller with an empty or out-of-bounds slice has nothing to anchor to;
44
+ // reporting 0 slots lets draw_close_tip take its existing early-out.
45
+ if (range_start >= total) return Anchor{0, false};
46
+ return Anchor{total - range_start, morphing && range_end >= total};
47
+ }
48
+
49
+ } // namespace vroom::tip_anchor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-vroom-chart",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Mobile-first Skia candlestick chart for React Native",
5
5
  "license": "MIT",
6
6
  "author": "Darion Welch",
@@ -83,6 +83,7 @@
83
83
  "typecheck": "tsc --noEmit",
84
84
  "test": "vitest run",
85
85
  "build": "tsup",
86
+ "check:podspec": "ruby scripts/check-podspec-portable.rb",
86
87
  "vendor:core": "node scripts/vendor-core.mjs"
87
88
  }
88
89
  }
@@ -1,16 +1,30 @@
1
1
  require "json"
2
+ require "pathname"
2
3
 
3
4
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
5
 
5
- # Resolve the absolute path to the @shopify/react-native-skia source.
6
- # We need it because RN-Skia ships its Skia headers via `#include "include/core/SkPicture.h"`,
7
- # which only resolves when the include path points at the source `cpp/` and `cpp/skia/`
8
- # directories — CocoaPods' flat Headers/Public/ layout loses the `include/...` prefix.
6
+ # Locate the @shopify/react-native-skia source. We need it because RN-Skia ships
7
+ # its Skia headers via `#include "include/core/SkPicture.h"`, which only resolves
8
+ # when the include path points at the source `cpp/` and `cpp/skia/` directories —
9
+ # CocoaPods' flat Headers/Public/ layout loses the `include/...` prefix.
10
+ #
11
+ # The paths must be emitted relative to $(PODS_TARGET_SRCROOT), never absolute.
12
+ # CocoaPods serializes this spec to `Pods/Local Podspecs/*.podspec.json` and hashes
13
+ # that file into Podfile.lock's SPEC CHECKSUMS, so an absolute path bakes the
14
+ # checkout root of whoever ran `pod install` into the lockfile and every other
15
+ # machine fails `pod install --deployment`. Resolving at spec-eval time (rather
16
+ # than hardcoding `../@shopify/react-native-skia`) keeps this correct under
17
+ # non-hoisted layouts such as pnpm's.
9
18
  skia_pkg_json = `node --print "require.resolve('@shopify/react-native-skia/package.json')"`.strip
19
+ raise "react-native-vroom-chart: could not resolve @shopify/react-native-skia" if skia_pkg_json.empty?
10
20
  skia_src_dir = File.dirname(skia_pkg_json)
11
- skia_cpp_dir = File.join(skia_src_dir, "cpp")
12
- skia_skia_dir = File.join(skia_src_dir, "cpp", "skia")
13
- skia_api_dir = File.join(skia_src_dir, "cpp", "api")
21
+ skia_rel_dir = File.join("$(PODS_TARGET_SRCROOT)",
22
+ Pathname.new(skia_src_dir).relative_path_from(Pathname.new(__dir__)).to_s)
23
+ skia_cpp_dir = File.join(skia_rel_dir, "cpp")
24
+ skia_skia_dir = File.join(skia_rel_dir, "cpp", "skia")
25
+ skia_api_dir = File.join(skia_rel_dir, "cpp", "api")
26
+ # Absolute is fine here: the result only selects a preprocessor-define string,
27
+ # so it never reaches the emitted spec.
14
28
  use_graphite = File.exist?(File.join(skia_src_dir, "libs", ".graphite"))
15
29
  skia_preprocessor_defs = use_graphite ?
16
30
  "$(inherited) SK_GRAPHITE=1 SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1 SK_DISABLE_LEGACY_SHAPER_FACTORY=1" :