1 /**
2 Copyright: Copyright (c) 2015-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.text.scale;
7 
8 immutable string[int] scales;
9 shared static this(){
10 	scales = [
11 	-24 : "y",
12 	-21 : "z",
13 	-18 : "a",
14 	-15 : "f",
15 	-12 : "p",
16 	-9  : "n",
17 	-6  : "u",
18 	-3  : "m",
19 	 0  : "",
20 	 3  : "K",
21 	 6  : "M",
22 	 9  : "G",
23 	 12 : "T",
24 	 15 : "P",
25 	 18 : "E",
26 	 21 : "Z",
27 	 24 : "Y",];
28 }
29 
30 int numDigitsInNumber(Num)(const Num val)
31 {
32 	import std.math: abs;
33 	ulong absVal = cast(ulong)abs(val);
34 	int numDigits = 1;
35 
36 	while (absVal >= 10)
37 	{
38 		absVal /= 10;
39 		++numDigits;
40 	}
41 
42 	return numDigits;
43 }
44 
45 unittest
46 {
47 	assert(numDigitsInNumber(0) == 1);
48 	assert(numDigitsInNumber(-1) == 1);
49 	assert(numDigitsInNumber(1) == 1);
50 	assert(numDigitsInNumber(ubyte.max) == 3);
51 	assert(numDigitsInNumber(9) == 1);
52 	assert(numDigitsInNumber(10) == 2);
53 	assert(numDigitsInNumber(11) == 2);
54 	assert(numDigitsInNumber(100) == 3);
55 	assert(numDigitsInNumber(ushort.max) == 5);
56 	assert(numDigitsInNumber(uint.max) == 10); // 4294967295
57 	assert(numDigitsInNumber(long.max) == 19);
58 	assert(numDigitsInNumber(ulong.max) == 20);
59 }
60 
61 int calcScale(Num)(Num val)
62 {
63 	import std.algorithm: clamp;
64 	import std.math: abs, floor, ceil, log10;
65 	import voxelman.math : sign;
66 
67 	auto lg = log10(abs(val));
68 	int logSign = sign(lg);
69 	double absLog = abs(lg);
70 
71 	int scale;
72 	if (lg < 0)
73 		scale = cast(int)(ceil(absLog/3.0))*3;
74 	else
75 		scale = cast(int)(floor(absLog/3.0))*3;
76 
77 	int clampedScale = clamp(scale * logSign, -24, 24);
78 
79 	return clampedScale;
80 }
81 
82 unittest
83 {
84 	assert(calcScale(0.000_000_001) == -9);
85 	assert(calcScale(0.000_000_01) == -9);
86 	assert(calcScale(0.000_000_1) == -9);
87 
88 	assert(calcScale(0.000_001) == -6);
89 	assert(calcScale(0.000_01) == -6);
90 	assert(calcScale(0.000_1) == -6);
91 
92 	assert(calcScale(0.001) == -3);
93 	assert(calcScale(0.01) == -3);
94 	assert(calcScale(0.1) == -3);
95 
96 	assert(calcScale(1.0) == 0);
97 	assert(calcScale(10.0) == 0);
98 	assert(calcScale(100.0) == 0);
99 
100 	assert(calcScale(1_000.0) == 3);
101 	assert(calcScale(10_000.0) == 3);
102 	assert(calcScale(100_000.0) == 3);
103 
104 	assert(calcScale(1_000_000.0) == 6);
105 	assert(calcScale(10_000_000.0) == 6);
106 	assert(calcScale(100_000_000.0) == 6);
107 
108 	assert(calcScale(1_000_000_000.0) == 9);
109 	assert(calcScale(10_000_000_000.0) == 9);
110 	assert(calcScale(100_000_000_000.0) == 9);
111 }
112 
113 struct ScaledNumberFmt(T)
114 {
115 	T value;
116 	void toString()(scope void delegate(const(char)[]) sink)
117 	{
118 		import std.format : formattedWrite;
119 		int scale = calcScale(value);
120 		auto scaledValue = scaled(value, scale);
121 		int digits = numDigitsInNumber(scaledValue);
122 		sink.formattedWrite("%*.*f%s", digits, 3-digits, scaledValue, scales[scale]);
123 	}
124 }
125 
126 /// Display number as
127 /// d.dds (1.23m)
128 /// dd.ds (12.3K)
129 /// ddds  (123G)
130 /// Where d is digit, s is SI suffix
131 auto scaledNumberFmt(T)(T value)
132 {
133 	return ScaledNumberFmt!T(value);
134 }
135 
136 import core.time : Duration;
137 auto scaledNumberFmt(Duration value)
138 {
139 	double seconds = value.total!"hnsecs" / 10_000_000.0;
140 	return ScaledNumberFmt!double(seconds);
141 }
142 /*
143 unittest
144 {
145 	import std.stdio;
146 	0.000_000_001234.scaledNumberFmt.writeln;
147 	0.000_000_01234.scaledNumberFmt.writeln;
148 	0.000_000_1234.scaledNumberFmt.writeln;
149 	0.000_001234.scaledNumberFmt.writeln;
150 	0.000_01234.scaledNumberFmt.writeln;
151 	0.000_1234.scaledNumberFmt.writeln;
152 	0.001234.scaledNumberFmt.writeln;
153 	0.01234.scaledNumberFmt.writeln;
154 	0.1234.scaledNumberFmt.writeln;
155 	1.234.scaledNumberFmt.writeln;
156 	12.34.scaledNumberFmt.writeln;
157 	123.4.scaledNumberFmt.writeln;
158 	1_234.0.scaledNumberFmt.writeln;
159 	12_340.0.scaledNumberFmt.writeln;
160 	123_400.0.scaledNumberFmt.writeln;
161 	1_234_000.0.scaledNumberFmt.writeln;
162 	12_340_000.0.scaledNumberFmt.writeln;
163 	123_400_000.0.scaledNumberFmt.writeln;
164 	1_234_000_000.0.scaledNumberFmt.writeln;
165 	12_340_000_000.0.scaledNumberFmt.writeln;
166 	123_400_000_000.0.scaledNumberFmt.writeln;
167 
168 	0.000_000_001.scaledNumberFmt.writeln;
169 	0.000_000_01.scaledNumberFmt.writeln;
170 	0.000_000_1.scaledNumberFmt.writeln;
171 	0.000_001.scaledNumberFmt.writeln;
172 	0.000_01.scaledNumberFmt.writeln;
173 	0.000_1.scaledNumberFmt.writeln;
174 	0.001.scaledNumberFmt.writeln;
175 	0.01.scaledNumberFmt.writeln;
176 	0.1.scaledNumberFmt.writeln;
177 	1.0.scaledNumberFmt.writeln;
178 	10.0.scaledNumberFmt.writeln;
179 	100.0.scaledNumberFmt.writeln;
180 	1_000.0.scaledNumberFmt.writeln;
181 	10_000.0.scaledNumberFmt.writeln;
182 	100_000.0.scaledNumberFmt.writeln;
183 	1_000_000.0.scaledNumberFmt.writeln;
184 	10_000_000.0.scaledNumberFmt.writeln;
185 	100_000_000.0.scaledNumberFmt.writeln;
186 	1_000_000_000.0.scaledNumberFmt.writeln;
187 	10_000_000_000.0.scaledNumberFmt.writeln;
188 	100_000_000_000.0.scaledNumberFmt.writeln;
189 }
190 */
191 double scaled(Num)(Num num, int scale)
192 {
193 	import std.math: pow;
194 	return num * pow(10.0, -scale);
195 }
196 
197 int stepPrecision(float step)
198 {
199 	import std.algorithm : clamp;
200 	import std.math: floor, log10;
201 	return clamp(-cast(int)floor(log10(step)), 0, 3);
202 }