react-native-vroom-chart 0.12.0 → 0.13.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.
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <algorithm>
|
|
4
4
|
#include <cmath>
|
|
5
|
+
#include <cstddef>
|
|
5
6
|
#include <cstdio>
|
|
6
7
|
|
|
7
8
|
namespace vroom {
|
|
@@ -15,6 +16,38 @@ constexpr size_t kPlainCap = 340;
|
|
|
15
16
|
constexpr char kGroupSeparator = ',';
|
|
16
17
|
constexpr int kGroupSize = 3;
|
|
17
18
|
|
|
19
|
+
constexpr double kMillion = 1e6;
|
|
20
|
+
constexpr double kBillion = 1e9;
|
|
21
|
+
constexpr double kTrillion = 1e12;
|
|
22
|
+
|
|
23
|
+
double compact_divisor(CompactScale scale) {
|
|
24
|
+
switch (scale) {
|
|
25
|
+
case CompactScale::Million: return kMillion;
|
|
26
|
+
case CompactScale::Billion: return kBillion;
|
|
27
|
+
case CompactScale::Trillion: return kTrillion;
|
|
28
|
+
case CompactScale::None: return 1.0;
|
|
29
|
+
}
|
|
30
|
+
return 1.0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
char compact_suffix(CompactScale scale) {
|
|
34
|
+
switch (scale) {
|
|
35
|
+
case CompactScale::Million: return 'M';
|
|
36
|
+
case CompactScale::Billion: return 'B';
|
|
37
|
+
case CompactScale::Trillion: return 'T';
|
|
38
|
+
case CompactScale::None: return '\0';
|
|
39
|
+
}
|
|
40
|
+
return '\0';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
CompactScale compact_scale_for(double reference) {
|
|
44
|
+
const double r = std::fabs(reference);
|
|
45
|
+
if (!std::isfinite(r) || r < kMillion) return CompactScale::None;
|
|
46
|
+
if (r >= kTrillion) return CompactScale::Trillion;
|
|
47
|
+
if (r >= kBillion) return CompactScale::Billion;
|
|
48
|
+
return CompactScale::Million;
|
|
49
|
+
}
|
|
50
|
+
|
|
18
51
|
} // namespace
|
|
19
52
|
|
|
20
53
|
int significant_decimals(double reference) {
|
|
@@ -28,7 +61,8 @@ int significant_decimals(double reference) {
|
|
|
28
61
|
}
|
|
29
62
|
|
|
30
63
|
PriceFormat price_format_for(double reference) {
|
|
31
|
-
return PriceFormat{significant_decimals(reference), true
|
|
64
|
+
return PriceFormat{significant_decimals(reference), true,
|
|
65
|
+
compact_scale_for(reference)};
|
|
32
66
|
}
|
|
33
67
|
|
|
34
68
|
int price_decimals(double interval) {
|
|
@@ -40,8 +74,13 @@ int price_decimals(double interval) {
|
|
|
40
74
|
}
|
|
41
75
|
|
|
42
76
|
PriceFormat with_tick_guard(const PriceFormat& fmt, double interval) {
|
|
43
|
-
|
|
44
|
-
|
|
77
|
+
const double divisor = compact_divisor(fmt.compact);
|
|
78
|
+
const double display_interval =
|
|
79
|
+
(divisor != 1.0 && interval > 0.0 && std::isfinite(interval))
|
|
80
|
+
? interval / divisor
|
|
81
|
+
: interval;
|
|
82
|
+
return PriceFormat{std::max(fmt.decimals, price_decimals(display_interval)),
|
|
83
|
+
fmt.group, fmt.compact};
|
|
45
84
|
}
|
|
46
85
|
|
|
47
86
|
void format_price(char* buf, size_t buf_size, double price,
|
|
@@ -49,11 +88,23 @@ void format_price(char* buf, size_t buf_size, double price,
|
|
|
49
88
|
if (!buf || buf_size == 0) return;
|
|
50
89
|
const int decimals = std::clamp(fmt.decimals, 0, kPriceMaxDecimals);
|
|
51
90
|
|
|
91
|
+
const double divisor = compact_divisor(fmt.compact);
|
|
92
|
+
const double display = (divisor != 1.0) ? price / divisor : price;
|
|
93
|
+
|
|
52
94
|
char plain[kPlainCap];
|
|
53
|
-
if (std::snprintf(plain, sizeof(plain), "%.*f", decimals,
|
|
95
|
+
if (std::snprintf(plain, sizeof(plain), "%.*f", decimals, display) < 0) {
|
|
54
96
|
buf[0] = '\0';
|
|
55
97
|
return;
|
|
56
98
|
}
|
|
99
|
+
|
|
100
|
+
const char suffix = compact_suffix(fmt.compact);
|
|
101
|
+
if (suffix != '\0') {
|
|
102
|
+
// Compact form is already short; grouping would only fire if a tick
|
|
103
|
+
// wandered across a suffix boundary (1000.00M). Skip it.
|
|
104
|
+
std::snprintf(buf, buf_size, "%s%c", plain, suffix);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
57
108
|
if (!fmt.group) {
|
|
58
109
|
std::snprintf(buf, buf_size, "%s", plain);
|
|
59
110
|
return;
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
// 0.023397 -> 6 decimals 0.023397
|
|
14
14
|
// 80285.20 -> 2 decimals 80,285.20 (floor)
|
|
15
15
|
// 2513.92 -> 2 decimals 2,513.92 (floor)
|
|
16
|
+
// 1.59e6 -> compact 1.59M
|
|
17
|
+
// 3.219e7 -> compact 32.19M
|
|
18
|
+
// 3.454e10 -> compact 34.54B
|
|
19
|
+
// 1.2e12 -> compact 1.20T
|
|
20
|
+
//
|
|
21
|
+
// Past a million the integer digits eat the axis, so the format switches to a
|
|
22
|
+
// suffix (M / B / T) with two decimal places. The suffix is keyed off the
|
|
23
|
+
// asset's own price, not each tick, so every label on a market-cap chart
|
|
24
|
+
// shares a column and a zoomed-in 32.1M–32.2M range does not mix "32.15M"
|
|
25
|
+
// with "32,150,000.00".
|
|
16
26
|
//
|
|
17
27
|
// The reference is the asset's own price, not the visible range, so the
|
|
18
28
|
// precision holds steady while the user pans and zooms. A range-derived count
|
|
@@ -32,18 +42,31 @@ namespace vroom {
|
|
|
32
42
|
inline constexpr int kPriceSigDigits = 5;
|
|
33
43
|
|
|
34
44
|
// 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).
|
|
45
|
+
// looks broken (a "$84,000" axis next to a "$80,285.20" last price). Compact
|
|
46
|
+
// suffixes use the same floor: "32.19M", not "32.2M".
|
|
36
47
|
inline constexpr int kPriceMinDecimals = 2;
|
|
37
48
|
|
|
38
49
|
// Past this the digits are noise, and %.*f starts printing the binary
|
|
39
50
|
// representation's tail rather than anything the feed meant.
|
|
40
51
|
inline constexpr int kPriceMaxDecimals = 12;
|
|
41
52
|
|
|
53
|
+
// Compact suffix for assets priced in the millions and up. Keyed off the
|
|
54
|
+
// reference, then applied to every label so the column stays uniform. Nothing
|
|
55
|
+
// below a million is abbreviated — no K.
|
|
56
|
+
enum class CompactScale {
|
|
57
|
+
None,
|
|
58
|
+
Million, // |ref| >= 1e6
|
|
59
|
+
Billion, // |ref| >= 1e9
|
|
60
|
+
Trillion, // |ref| >= 1e12
|
|
61
|
+
};
|
|
62
|
+
|
|
42
63
|
// How to render a price. `group` inserts thousands separators, which only ever
|
|
43
|
-
// affects the integer part.
|
|
64
|
+
// affects the integer part of a non-compact format. `compact` scales the value
|
|
65
|
+
// and appends M / B / T; grouping is skipped in that path.
|
|
44
66
|
struct PriceFormat {
|
|
45
|
-
int
|
|
46
|
-
bool
|
|
67
|
+
int decimals = kPriceMinDecimals;
|
|
68
|
+
bool group = true;
|
|
69
|
+
CompactScale compact = CompactScale::None;
|
|
47
70
|
};
|
|
48
71
|
|
|
49
72
|
// Decimals giving kPriceSigDigits significant digits for an asset priced around
|
|
@@ -51,7 +74,8 @@ struct PriceFormat {
|
|
|
51
74
|
// non-finite reference falls back to the floor.
|
|
52
75
|
int significant_decimals(double reference);
|
|
53
76
|
|
|
54
|
-
// The format for an asset priced around `reference
|
|
77
|
+
// The format for an asset priced around `reference`, including the compact
|
|
78
|
+
// suffix when |reference| is at least a million.
|
|
55
79
|
PriceFormat price_format_for(double reference);
|
|
56
80
|
|
|
57
81
|
// Decimal places needed so adjacent ticks `interval` apart don't collapse to
|
|
@@ -60,6 +84,9 @@ int price_decimals(double interval);
|
|
|
60
84
|
|
|
61
85
|
// `fmt` raised, if need be, so ticks `interval` apart stay distinct. The asset's
|
|
62
86
|
// precision is the floor; only a zoom deeper than that precision adds to it.
|
|
87
|
+
// `interval` is in the data's units; when `fmt` is compact the guard converts
|
|
88
|
+
// it into display units (interval / 1e6 for millions, etc.) so a 1,000-wide
|
|
89
|
+
// step on a 32M chart still gets enough decimals to print as 32.192M vs 32.193M.
|
|
63
90
|
PriceFormat with_tick_guard(const PriceFormat& fmt, double interval);
|
|
64
91
|
|
|
65
92
|
// Writes `price` into `buf` per `fmt`, truncating rather than overrunning.
|