Created
April 15, 2020 11:34
-
-
Save vegard/35c5b34eb6d6c5d0c2d51cd5ee2fc7ee to your computer and use it in GitHub Desktop.
Float to byte quantisation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 0 | |
(g++-9 $0 || g++ $0) && \ | |
./a.out > output.tex && \ | |
pdflatex output && \ | |
exec convert -density 400 -flatten output.pdf -resize 25% output.png | |
exit 1 | |
#endif | |
#include <cmath> | |
#include <cstdio> | |
#include <functional> | |
#include <vector> | |
static float clamp(float x, float a, float b) | |
{ | |
if (x < a) | |
return a; | |
if (x > b) | |
return b; | |
return x; | |
} | |
static float lerp(float a, float b, float t) | |
{ | |
return a * (1.f - t) + b * t; | |
} | |
struct method { | |
const char *name; | |
std::function<int(float)> fn; | |
}; | |
#define DEF(expr) \ | |
method { \ | |
.name = #expr, \ | |
.fn = [](float x) { \ | |
return (expr); \ | |
}, \ | |
} | |
// https://twitter.com/rygorous/status/1249876256599846912 | |
// https://stackoverflow.com/questions/1914115/converting-color-value-from-float-0-1-to-byte-0-255 | |
static method methods[] = { | |
DEF(roundf(255.f * x + .5f)), | |
DEF(roundf(256.f * x)), | |
DEF(roundf(255.f * x)), | |
DEF(floorf(255.f * x + .5f)), | |
DEF(lerp(0.f, 255.f, x)), | |
DEF(floorf(255.f * x)), | |
DEF(lerp(0.f, 256.f, x)), | |
DEF(floorf(256.f * x)), | |
DEF(clamp(256.f * x, 0.f, 255.f)), | |
DEF(x >= 1.f ? 255.f : (256.f * x)), | |
DEF(floor(x >= 1.f ? 255.f : (256.f * x))), | |
DEF(x * 255.999f), | |
DEF(nextafterf(256.f, 0.f) * x), | |
}; | |
// find the smallest x in [L, R] such that fn(x) == target | |
float find_prev(std::function<int(float)> &fn, float L, float R, int target) | |
{ | |
float m = .5 * L + .5 * R; | |
while (true) { | |
if (L == R) | |
return L; | |
int v = fn(m); | |
//printf("%f < %f < %f : %d %d\n", L, m, R, v, target); | |
if (v < target) | |
L = m; | |
else if (v >= target) | |
R = m; | |
// can't make any more progress? return | |
float new_m = .5 * L + .5 * R; | |
if (new_m == m) | |
return R; | |
m = new_m; | |
} | |
} | |
// find the largest x in [L, R] such that fn(x) == target | |
float find_next(std::function<int(float)> &fn, float L, float R, int target) | |
{ | |
float m = .5 * L + .5 * R; | |
while (true) { | |
if (L == R) | |
return L; | |
int v = fn(m); | |
//printf("%f < %f < %f : %d %d\n", L, m, R, v, target); | |
if (v <= target) | |
L = m; | |
else if (v > target) | |
R = m; | |
// can't make any more progress? return | |
float new_m = .5 * L + .5 * R; | |
if (new_m == m) | |
return L; | |
m = new_m; | |
} | |
} | |
static float xin0, xin1; | |
static float xout0, xout1; | |
static float in_to_out(float x) | |
{ | |
return xout0 + (xout1 - xout0) * (x - xin0) / (xin1 - xin0); | |
} | |
static void draw_header() | |
{ | |
int first = floor(xin0 * 256); | |
int last = ceil(xin1 * 256); | |
printf("\\draw (%f, 0) -- coordinate (x axis mid) (%f, 0);\n", in_to_out(first / 256.f), in_to_out(last / 256.f)); | |
// put tick marks every 1/256. point | |
for (int x = first; x <= last; ++x) { | |
float xout = in_to_out(x / 256.f); | |
// prevent edge clipping | |
if (xout > xout0 + .2 && xout < xout1 - .2) { | |
printf("\\draw (%f, 1pt) -- (%f, -3pt) node[anchor=north] {$\\displaystyle\\frac{%d}{256}$};\n", xout, xout, x); | |
} | |
} | |
} | |
static void draw_diagram(unsigned int y, struct method &m, const std::vector<int> &targets, const char *color) | |
{ | |
int tmin = targets.front(); | |
int tmax = targets.back(); | |
for (unsigned int i = 0; i < targets.size(); ++i) { | |
int t = targets[i]; | |
float x_prev = find_prev(m.fn, -1.f, 2.f, t); | |
float x_next = find_next(m.fn, -1.f, 2.f, t); | |
// background colour | |
printf("\\fill[%s!%d!black!90] (%f, %u) rectangle (%f, %u) node[midway] {};\n", | |
color, (int) roundf(20 + 60.f * (t - tmin) / (tmax - tmin)), | |
in_to_out(x_prev), y, in_to_out(x_next), y + 1); | |
// text | |
{ | |
float text_xout0 = in_to_out(fmaxf(0.f, x_prev)); | |
float text_xout1 = in_to_out(fminf(1.f + 1.f / 256.f, x_next)); | |
if (x_next > 1.1) | |
text_xout1 = in_to_out(fminf(1.f, x_next)); | |
if (text_xout0 > xout0 - .1 | |
&& text_xout1 < xout1 + .1 | |
&& text_xout1 - text_xout0 > .5) | |
{ | |
// draw the number in the center of the node | |
printf("\\fill[fill=none,color=white] (%f, %u) rectangle (%f, %u) node[midway] {\\textbf{%d}};\n", | |
text_xout0, y, text_xout1, y + 1, t); | |
} | |
} | |
} | |
} | |
int main(int argc, char *argv[]) | |
{ | |
unsigned int nr_methods = sizeof(methods) / sizeof(*methods); | |
printf("\\documentclass{standalone}\n"); | |
printf("\\usepackage{charter}\n"); | |
printf("\\usepackage{tikz}\n"); | |
printf("\\begin{document}\n"); | |
printf("\\begin{tikzpicture}[scale=1.5]\n"); | |
{ | |
// region to draw on the number line | |
xin0 = -.5f / 256; | |
xin1 = 3.5f / 256; | |
// region to draw on in tikz | |
xout0 = 0; | |
xout1 = 0 + 4; | |
printf("\\begin{scope}\n"); | |
printf("\\clip (%f, -1) rectangle (%f, %u);\n", xout0, xout1, nr_methods + 1); | |
draw_header(); | |
for (unsigned int i = 0; i < nr_methods; ++i) { | |
draw_diagram(nr_methods - i - 1, methods[i], { -1, 0, 1, 2, 3, 4 }, "red"); | |
} | |
printf("\\end{scope}\n"); | |
} | |
{ | |
// region to draw on the number line | |
xin0 = .5f - 2.f / 256; | |
xin1 = .5f + 2.f / 256; | |
// region to draw on in tikz | |
xout0 = 4.5; | |
xout1 = 4.5 + 4; | |
printf("\\begin{scope}\n"); | |
printf("\\clip (%f, -1) rectangle (%f, %u);\n", xout0, xout1, nr_methods + 1); | |
draw_header(); | |
for (unsigned int i = 0; i < nr_methods; ++i) { | |
draw_diagram(nr_methods - i - 1, methods[i], { 125, 126, 127, 128, 129, 130 }, "green"); | |
} | |
printf("\\end{scope}\n"); | |
} | |
{ | |
// region to draw on the number line | |
xin0 = 1. - 3.5f / 256; | |
xin1 = 1. + .5f / 256; | |
// region to draw on in tikz | |
xout0 = 9; | |
xout1 = 9 + 4; | |
printf("\\begin{scope}\n"); | |
printf("\\clip (%f, -1) rectangle (%f, %u);\n", xout0, xout1, nr_methods + 1); | |
draw_header(); | |
for (unsigned int i = 0; i < nr_methods; ++i) { | |
draw_diagram(nr_methods - i - 1, methods[i], { 251, 252, 253, 254, 255, 256 }, "blue"); | |
} | |
printf("\\end{scope}\n"); | |
} | |
// names | |
{ | |
// region to draw on in tikz | |
xout0 = 13.5f; | |
xout1 = 13.5f + 5; | |
printf("\\begin{scope}\n"); | |
printf("\\clip (%f, -1) rectangle (%f, %u);\n", xout0, xout1, nr_methods + 1); | |
for (unsigned int i = 0; i < nr_methods; ++i) { | |
printf("\\node[anchor=west] at (%f, %f) { \\texttt{%s} };\n", xout0, nr_methods - i - 1 + .5f, methods[i].name); | |
} | |
printf("\\end{scope}\n"); | |
} | |
printf("\\end{tikzpicture}\n"); | |
printf("\\end{document}\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output: