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 }