react-native-vroom-chart 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/CMakeLists.txt +8 -0
- package/android/README.md +11 -10
- package/cpp/VroomChartHostObject.cpp +155 -54
- package/cpp/VroomChartHostObject.h +1 -1
- package/cpp/VroomJsiInstaller.cpp +1 -1
- package/cpp/_core_include/vroom/vroom_chart.h +6 -7
- package/cpp/_core_src/candles.h +7 -5
- package/cpp/_core_src/chart.cpp +271 -152
- package/cpp/_core_src/chart.h +6 -3
- package/cpp/_core_src/chart_facade.cpp +5 -1
- package/cpp/_core_src/viewport.h +4 -0
- package/lib/index.d.mts +52 -6
- package/lib/index.d.ts +52 -6
- package/lib/index.js +68 -36
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +77 -38
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +94 -44
- package/src/index.ts +2 -0
- package/src/jsi.d.ts +18 -17
- package/src/types.ts +2 -0
- package/src/useChartCore.ts +11 -7
package/android/CMakeLists.txt
CHANGED
|
@@ -124,6 +124,14 @@ target_compile_options(vroomchart PRIVATE -Wno-mismatched-tags)
|
|
|
124
124
|
|
|
125
125
|
find_library(VROOM_LOG_LIB log)
|
|
126
126
|
|
|
127
|
+
# Android 15+ devices can use 16KB pages. NDK r27 and older default to 4KB
|
|
128
|
+
# ELF alignment (align=2**12); Play/SDK 35 then rejects libvroomchart.so.
|
|
129
|
+
# r28+ already aligns to 16KB, so these flags are redundant there but harmless.
|
|
130
|
+
target_link_options(vroomchart PRIVATE
|
|
131
|
+
"-Wl,-z,max-page-size=16384"
|
|
132
|
+
"-Wl,-z,common-page-size=16384"
|
|
133
|
+
)
|
|
134
|
+
|
|
127
135
|
target_link_libraries(vroomchart
|
|
128
136
|
${VROOM_LOG_LIB}
|
|
129
137
|
ReactAndroid::jsi
|
package/android/README.md
CHANGED
|
@@ -52,13 +52,14 @@ different, unmerged RTTI record for "the same" class and fails with
|
|
|
52
52
|
`Expected a Skia object of a different type`.
|
|
53
53
|
|
|
54
54
|
`VroomChartHostObject.cpp`'s `wrapPicture()` works around this only on
|
|
55
|
-
Android
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
Android. Two copies of Skia cannot share an `sk_sp<SkPicture>` pointer, and
|
|
56
|
+
serializing the chart picture (the original workaround) embedded the Android
|
|
57
|
+
system typeface on every pan/zoom frame — an OOM that killed the process after
|
|
58
|
+
a few seconds of gesturing.
|
|
59
|
+
|
|
60
|
+
The hot path now rasterizes the picture in vroom's Skia and hands RN-Skia raw
|
|
61
|
+
pixels via `Skia.Image.MakeImage`, so `<Image>` paints a framebuffer whose size
|
|
62
|
+
is bounded by the view. If rasterization fails, we still serialize, but with
|
|
63
|
+
`SkTypeface::SerializeBehavior::kDontIncludeData` so the font file is not
|
|
64
|
+
copied; `MakePicture` re-resolves `sans-serif` inside `librnskia.so`. iOS is
|
|
65
|
+
unchanged (one binary, direct `JsiSkPicture` wrap).
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include <string>
|
|
5
5
|
#include <vector>
|
|
6
6
|
|
|
7
|
+
#include "chart.h"
|
|
7
8
|
#include "chart_internal.h"
|
|
8
9
|
#include "vroom/vroom_chart.h"
|
|
9
10
|
|
|
@@ -11,6 +12,18 @@
|
|
|
11
12
|
#include "JsiSkNativeObjects.h"
|
|
12
13
|
#include "JsiSkPicture.h"
|
|
13
14
|
|
|
15
|
+
#if defined(__ANDROID__)
|
|
16
|
+
#include <algorithm>
|
|
17
|
+
#include <cmath>
|
|
18
|
+
|
|
19
|
+
#include "include/core/SkBitmap.h"
|
|
20
|
+
#include "include/core/SkCanvas.h"
|
|
21
|
+
#include "include/core/SkData.h"
|
|
22
|
+
#include "include/core/SkImageInfo.h"
|
|
23
|
+
#include "include/core/SkSerialProcs.h"
|
|
24
|
+
#include "include/core/SkTypeface.h"
|
|
25
|
+
#endif
|
|
26
|
+
|
|
14
27
|
namespace vroom {
|
|
15
28
|
|
|
16
29
|
namespace jsi = facebook::jsi;
|
|
@@ -74,27 +87,21 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
|
|
|
74
87
|
}
|
|
75
88
|
|
|
76
89
|
#if defined(__ANDROID__)
|
|
77
|
-
// Android only: RN-Skia
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// `getJsiObject<JsiSkPicture>(rt, value)` — a dynamic_pointer_cast
|
|
83
|
-
// under the hood), the cast fails cross-.so with "Expected a Skia object of
|
|
84
|
-
// a different type", because the object's *runtime* type was never actually
|
|
85
|
-
// compiled inside librnskia.so. iOS statically links everything into one
|
|
86
|
-
// binary, so no such mismatch exists there.
|
|
90
|
+
// Android only: RN-Skia lives in librnskia.so, vroom in libvroomchart.so, and
|
|
91
|
+
// each statically links its own libskia.a. A JsiSkPicture constructed here has
|
|
92
|
+
// our .so's RTTI, so RN-Skia's dynamic_pointer_cast fails with "Expected a
|
|
93
|
+
// Skia object of a different type". iOS statically links one binary, so the
|
|
94
|
+
// iOS wrap below can hand the sk_sp<SkPicture> through directly.
|
|
87
95
|
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
// Crossing that boundary used to serialize the chart picture and call
|
|
97
|
+
// Skia.Picture.MakePicture(bytes). Default SkPicture::serialize() embeds the
|
|
98
|
+
// Android system typeface (Noto-scale, megabytes) on every pan/zoom frame;
|
|
99
|
+
// Hermes never kept up and the process was OOM-killed (~3 GB RSS in ~10s).
|
|
100
|
+
//
|
|
101
|
+
// Preferred path: rasterize in *our* Skia and hand RN-Skia raw pixels via
|
|
102
|
+
// Skia.Image.MakeImage — memory is one framebuffer, labels stay correct.
|
|
103
|
+
// Fallback: still serialize, but with kDontIncludeData so the font file is
|
|
104
|
+
// not copied; MakePicture re-resolves sans-serif on RN-Skia's side.
|
|
98
105
|
namespace {
|
|
99
106
|
class SkDataMutableBuffer : public facebook::jsi::MutableBuffer {
|
|
100
107
|
public:
|
|
@@ -108,41 +115,119 @@ class SkDataMutableBuffer : public facebook::jsi::MutableBuffer {
|
|
|
108
115
|
private:
|
|
109
116
|
sk_sp<SkData> data_;
|
|
110
117
|
};
|
|
111
|
-
} // namespace
|
|
112
118
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (!
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
jsi::Value skiaApiObject(jsi::Runtime& rt, const char* name) {
|
|
120
|
+
auto skiaApi = rt.global().getProperty(rt, "SkiaApi");
|
|
121
|
+
if (!skiaApi.isObject()) return jsi::Value::undefined();
|
|
122
|
+
auto prop = skiaApi.asObject(rt).getProperty(rt, name);
|
|
123
|
+
return prop.isObject() ? std::move(prop) : jsi::Value::undefined();
|
|
124
|
+
}
|
|
118
125
|
|
|
119
|
-
|
|
126
|
+
// Uint8Array-shaped `{ buffer }` — MakePicture and Data.fromBytes both
|
|
127
|
+
// read the ArrayBuffer off that property.
|
|
128
|
+
jsi::Object bytesLike(jsi::Runtime& rt, sk_sp<SkData> data) {
|
|
129
|
+
auto buffer = std::make_shared<SkDataMutableBuffer>(std::move(data));
|
|
120
130
|
jsi::ArrayBuffer arrayBuffer(rt, buffer);
|
|
131
|
+
jsi::Object arg(rt);
|
|
132
|
+
arg.setProperty(rt, "buffer", arrayBuffer);
|
|
133
|
+
return arg;
|
|
134
|
+
}
|
|
121
135
|
|
|
122
|
-
|
|
123
|
-
if (!
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
sk_sp<const SkData> serializeTypefaceNoEmbed(SkTypeface* tf, void*) {
|
|
137
|
+
if (!tf) return nullptr;
|
|
138
|
+
return tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
jsi::Value wrapSerializedPicture(jsi::Runtime& rt, const sk_sp<SkPicture>& pic) {
|
|
142
|
+
SkSerialProcs procs;
|
|
143
|
+
procs.fTypefaceProc = serializeTypefaceNoEmbed;
|
|
144
|
+
sk_sp<SkData> serialized = pic->serialize(&procs);
|
|
145
|
+
if (!serialized) return jsi::Value::null();
|
|
146
|
+
|
|
147
|
+
auto pictureFactory = skiaApiObject(rt, "Picture");
|
|
148
|
+
if (pictureFactory.isUndefined()) return jsi::Value::null();
|
|
126
149
|
auto pictureFactoryObj = pictureFactory.asObject(rt);
|
|
127
150
|
auto makePicture =
|
|
128
151
|
pictureFactoryObj.getPropertyAsFunction(rt, "MakePicture");
|
|
129
152
|
|
|
130
|
-
// Mirrors JsiSkPictureFactory::MakePicture's expected argument shape: an
|
|
131
|
-
// object with a `.buffer` property holding the ArrayBuffer (matching a
|
|
132
|
-
// Uint8Array-like value; the factory ignores everything else about it).
|
|
133
|
-
//
|
|
134
153
|
// Skia 2.11's NativeState host methods recover the factory via `this`
|
|
135
154
|
// (`fromThis` → "Expected PictureFactory but got non-object" if unbound).
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
return makePicture.callWithThis(rt, pictureFactoryObj,
|
|
156
|
+
bytesLike(rt, std::move(serialized)));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
jsi::Value wrapRasterImage(jsi::Runtime& rt, const sk_sp<SkPicture>& pic,
|
|
160
|
+
float dpr) {
|
|
161
|
+
const SkRect cull = pic->cullRect();
|
|
162
|
+
const float scale = dpr > 0.f ? dpr : 1.f;
|
|
163
|
+
const int w =
|
|
164
|
+
std::max(1, static_cast<int>(std::ceil(cull.width() * scale)));
|
|
165
|
+
const int h =
|
|
166
|
+
std::max(1, static_cast<int>(std::ceil(cull.height() * scale)));
|
|
167
|
+
|
|
168
|
+
// Reused across frames on this JS thread so pan/zoom doesn't heap-allocate
|
|
169
|
+
// a new bitmap every time. Multiple charts share it; a size change reallocs.
|
|
170
|
+
thread_local SkBitmap raster;
|
|
171
|
+
const auto info = SkImageInfo::MakeN32Premul(w, h);
|
|
172
|
+
if ((raster.width() != w || raster.height() != h) &&
|
|
173
|
+
!raster.tryAllocPixels(info)) {
|
|
174
|
+
return jsi::Value::null();
|
|
175
|
+
}
|
|
176
|
+
raster.eraseColor(SK_ColorTRANSPARENT);
|
|
177
|
+
SkCanvas canvas(raster);
|
|
178
|
+
canvas.scale(scale, scale);
|
|
179
|
+
canvas.translate(-cull.left(), -cull.top());
|
|
180
|
+
canvas.drawPicture(pic);
|
|
181
|
+
|
|
182
|
+
SkPixmap pixmap;
|
|
183
|
+
if (!raster.peekPixels(&pixmap) || pixmap.addr() == nullptr) {
|
|
184
|
+
return jsi::Value::null();
|
|
185
|
+
}
|
|
186
|
+
sk_sp<SkData> pixels =
|
|
187
|
+
SkData::MakeWithCopy(pixmap.addr(), pixmap.computeByteSize());
|
|
188
|
+
if (!pixels) return jsi::Value::null();
|
|
189
|
+
|
|
190
|
+
auto dataFactory = skiaApiObject(rt, "Data");
|
|
191
|
+
auto imageFactory = skiaApiObject(rt, "Image");
|
|
192
|
+
if (dataFactory.isUndefined() || imageFactory.isUndefined()) {
|
|
193
|
+
return jsi::Value::null();
|
|
194
|
+
}
|
|
195
|
+
auto dataFactoryObj = dataFactory.asObject(rt);
|
|
196
|
+
auto imageFactoryObj = imageFactory.asObject(rt);
|
|
197
|
+
auto fromBytes = dataFactoryObj.getPropertyAsFunction(rt, "fromBytes");
|
|
198
|
+
auto makeImage = imageFactoryObj.getPropertyAsFunction(rt, "MakeImage");
|
|
199
|
+
|
|
200
|
+
auto skData =
|
|
201
|
+
fromBytes.callWithThis(rt, dataFactoryObj, bytesLike(rt, std::move(pixels)));
|
|
202
|
+
if (!skData.isObject()) return jsi::Value::null();
|
|
203
|
+
|
|
204
|
+
jsi::Object imageInfo(rt);
|
|
205
|
+
imageInfo.setProperty(rt, "width", static_cast<double>(w));
|
|
206
|
+
imageInfo.setProperty(rt, "height", static_cast<double>(h));
|
|
207
|
+
imageInfo.setProperty(rt, "colorType",
|
|
208
|
+
static_cast<double>(pixmap.colorType()));
|
|
209
|
+
imageInfo.setProperty(rt, "alphaType",
|
|
210
|
+
static_cast<double>(pixmap.alphaType()));
|
|
211
|
+
return makeImage.callWithThis(rt, imageFactoryObj, imageInfo, skData,
|
|
212
|
+
static_cast<double>(pixmap.rowBytes()));
|
|
213
|
+
}
|
|
214
|
+
} // namespace
|
|
215
|
+
|
|
216
|
+
static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
|
|
217
|
+
const sk_sp<SkPicture>& pic,
|
|
218
|
+
float dpr = 1.f) {
|
|
219
|
+
if (!pic) return facebook::jsi::Value::null();
|
|
220
|
+
auto image = wrapRasterImage(rt, pic, dpr);
|
|
221
|
+
if (!image.isNull()) return image;
|
|
222
|
+
return wrapSerializedPicture(rt, pic);
|
|
139
223
|
}
|
|
140
224
|
#else
|
|
141
225
|
// Shared helper: wraps a fresh picture for return to JS. Memory pressure is
|
|
142
226
|
// applied inside JsiSkPicture::create (NativeObject::create reads
|
|
143
227
|
// getMemoryPressure() → picture->approximateBytesUsed()).
|
|
144
228
|
static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
|
|
145
|
-
const sk_sp<SkPicture>& pic
|
|
229
|
+
const sk_sp<SkPicture>& pic,
|
|
230
|
+
float /*dpr*/ = 1.f) {
|
|
146
231
|
if (!pic) return facebook::jsi::Value::null();
|
|
147
232
|
auto host =
|
|
148
233
|
std::make_shared<RNSkia::JsiSkPicture>(/*context=*/nullptr, pic);
|
|
@@ -399,17 +484,25 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
399
484
|
}
|
|
400
485
|
|
|
401
486
|
if (name == "beginIntervalMorph") {
|
|
402
|
-
// beginIntervalMorph() — capture the visible candle geometry so the
|
|
403
|
-
// setCandles can
|
|
487
|
+
// beginIntervalMorph(mode?) — capture the visible candle geometry so the
|
|
488
|
+
// next setCandles can animate. Optional string `'fade'` or `'transform'`
|
|
489
|
+
// (default). Call before setCandles.
|
|
404
490
|
return jsi::Function::createFromHostFunction(
|
|
405
491
|
rt,
|
|
406
492
|
jsi::PropNameID::forAscii(rt, "beginIntervalMorph"),
|
|
407
|
-
|
|
408
|
-
[this](jsi::Runtime&
|
|
493
|
+
1,
|
|
494
|
+
[this](jsi::Runtime& rt2,
|
|
409
495
|
const jsi::Value& /*thisVal*/,
|
|
410
|
-
const jsi::Value*
|
|
411
|
-
size_t
|
|
412
|
-
|
|
496
|
+
const jsi::Value* args,
|
|
497
|
+
size_t count) -> jsi::Value {
|
|
498
|
+
int32_t mode = 0;
|
|
499
|
+
if (count >= 1 && args[0].isString()) {
|
|
500
|
+
const auto s = args[0].asString(rt2).utf8(rt2);
|
|
501
|
+
mode = s == "fade" ? 1 : 0;
|
|
502
|
+
} else if (count >= 1 && args[0].isNumber()) {
|
|
503
|
+
mode = args[0].asNumber() != 0 ? 1 : 0;
|
|
504
|
+
}
|
|
505
|
+
vroom_chart_begin_interval_morph(chart_, mode);
|
|
413
506
|
return jsi::Value::undefined();
|
|
414
507
|
});
|
|
415
508
|
}
|
|
@@ -448,7 +541,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
448
541
|
const float dx = static_cast<float>(args[0].asNumber());
|
|
449
542
|
const float dy = static_cast<float>(args[1].asNumber());
|
|
450
543
|
vroom_chart_pan(chart_, dx, dy);
|
|
451
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
544
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
545
|
+
chart_->px_ratio);
|
|
452
546
|
});
|
|
453
547
|
}
|
|
454
548
|
|
|
@@ -467,7 +561,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
467
561
|
const float dx = static_cast<float>(args[0].asNumber());
|
|
468
562
|
const float dy = static_cast<float>(args[1].asNumber());
|
|
469
563
|
vroom_chart_translate(chart_, dx, dy);
|
|
470
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
564
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
565
|
+
chart_->px_ratio);
|
|
471
566
|
});
|
|
472
567
|
}
|
|
473
568
|
|
|
@@ -489,7 +584,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
489
584
|
const float fx = static_cast<float>(args[2].asNumber());
|
|
490
585
|
const float fy = static_cast<float>(args[3].asNumber());
|
|
491
586
|
vroom_chart_zoom(chart_, sx, sy, fx, fy);
|
|
492
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
587
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
588
|
+
chart_->px_ratio);
|
|
493
589
|
});
|
|
494
590
|
}
|
|
495
591
|
|
|
@@ -505,7 +601,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
505
601
|
if (count < 1) return jsi::Value::null();
|
|
506
602
|
const float dy = static_cast<float>(args[0].asNumber());
|
|
507
603
|
vroom_chart_scale_price_axis(chart_, dy);
|
|
508
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
604
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
605
|
+
chart_->px_ratio);
|
|
509
606
|
});
|
|
510
607
|
}
|
|
511
608
|
|
|
@@ -521,7 +618,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
521
618
|
if (count < 1) return jsi::Value::null();
|
|
522
619
|
const float dx = static_cast<float>(args[0].asNumber());
|
|
523
620
|
vroom_chart_scale_time_axis(chart_, dx);
|
|
524
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
621
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
622
|
+
chart_->px_ratio);
|
|
525
623
|
});
|
|
526
624
|
}
|
|
527
625
|
|
|
@@ -572,7 +670,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
572
670
|
const float x = static_cast<float>(args[0].asNumber());
|
|
573
671
|
const float y = static_cast<float>(args[1].asNumber());
|
|
574
672
|
vroom_chart_set_crosshair(chart_, x, y);
|
|
575
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
673
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
674
|
+
chart_->px_ratio);
|
|
576
675
|
});
|
|
577
676
|
}
|
|
578
677
|
|
|
@@ -587,7 +686,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
587
686
|
const jsi::Value* /*args*/,
|
|
588
687
|
size_t /*count*/) -> jsi::Value {
|
|
589
688
|
vroom_chart_clear_crosshair(chart_);
|
|
590
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
689
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
690
|
+
chart_->px_ratio);
|
|
591
691
|
});
|
|
592
692
|
}
|
|
593
693
|
|
|
@@ -1098,7 +1198,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
|
|
|
1098
1198
|
const jsi::Value& /*thisVal*/,
|
|
1099
1199
|
const jsi::Value* /*args*/,
|
|
1100
1200
|
size_t /*count*/) -> jsi::Value {
|
|
1101
|
-
return wrapPicture(rt2, render_chart_picture(chart_)
|
|
1201
|
+
return wrapPicture(rt2, render_chart_picture(chart_),
|
|
1202
|
+
chart_->px_ratio);
|
|
1102
1203
|
});
|
|
1103
1204
|
}
|
|
1104
1205
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Methods exposed to JS:
|
|
7
7
|
// setCandles(arrayBuffer) — copies packed VroomCandle[] into the core
|
|
8
8
|
// setSize(width, height, pxRatio)
|
|
9
|
-
// render() ->
|
|
9
|
+
// render() -> SkPicture (iOS) / SkImage (Android)
|
|
10
10
|
|
|
11
11
|
#pragma once
|
|
12
12
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// methods on the returned object:
|
|
6
6
|
// setCandles(arrayBuffer)
|
|
7
7
|
// setSize(w, h, pxRatio)
|
|
8
|
-
// render() ->
|
|
8
|
+
// render() -> SkPicture (iOS) / SkImage (Android)
|
|
9
9
|
//
|
|
10
10
|
// The host object's lifetime is managed by JS GC — when the React component
|
|
11
11
|
// drops its reference, the underlying VroomChart is destroyed.
|
|
@@ -420,14 +420,13 @@ void vroom_chart_preserve_price_envelope(VroomChart* chart,
|
|
|
420
420
|
double prev_low, double prev_high);
|
|
421
421
|
|
|
422
422
|
// Captures the currently visible candle geometry so the next data swap can be
|
|
423
|
-
// animated
|
|
424
|
-
//
|
|
423
|
+
// animated. `mode` 0 (transform) lerps each visible column into its counterpart,
|
|
424
|
+
// paired by slot counting back from the right edge. `mode` 1 (fade) fades the
|
|
425
|
+
// capture out then the new scene in on the same envelope the axes already use.
|
|
425
426
|
//
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
|
|
429
|
-
// No-op when nothing is visible.
|
|
430
|
-
void vroom_chart_begin_interval_morph(VroomChart* chart);
|
|
427
|
+
// Call before set_candles, then drive vroom_chart_set_interval_morph from 0
|
|
428
|
+
// to 1. No-op when nothing is visible.
|
|
429
|
+
void vroom_chart_begin_interval_morph(VroomChart* chart, int32_t mode);
|
|
431
430
|
|
|
432
431
|
// Advances the interval morph started by vroom_chart_begin_interval_morph. `t`
|
|
433
432
|
// (clamped to 0..1) is the eased progress: 0 renders the captured geometry
|
package/cpp/_core_src/candles.h
CHANGED
|
@@ -28,11 +28,13 @@ namespace vroom::candles {
|
|
|
28
28
|
// body/wick heights and width shrink to the line). `opacity` (0..1) fades the
|
|
29
29
|
// whole candle layer out as the line fades in. Both default to a no-op.
|
|
30
30
|
//
|
|
31
|
-
// `from` / `from_n` is the outgoing geometry of an interval
|
|
32
|
-
// the right of the visible slice (slot 0 = newest), and `morph_t` (0..1)
|
|
33
|
-
// eased progress toward `visible`. Each slot's wick and body interpolate
|
|
34
|
-
// the two; slots present on only one side fade in or out.
|
|
35
|
-
//
|
|
31
|
+
// `from` / `from_n` is the outgoing geometry of an interval *transform*, indexed
|
|
32
|
+
// from the right of the visible slice (slot 0 = newest), and `morph_t` (0..1)
|
|
33
|
+
// is the eased progress toward `visible`. Each slot's wick and body interpolate
|
|
34
|
+
// between the two; slots present on only one side fade in or out. A host-chosen
|
|
35
|
+
// fade (see interval_morph_fade) skips this lerp: the chart draws the capture
|
|
36
|
+
// then the new scene via interval_phase instead. `morph_t == 1` (the default)
|
|
37
|
+
// draws `visible` alone.
|
|
36
38
|
void draw(SkCanvas* canvas,
|
|
37
39
|
const ::VroomCandle* visible,
|
|
38
40
|
std::size_t n,
|