Skip to content

Instantly share code, notes, and snippets.

@vegard
Created April 15, 2020 11:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vegard/35c5b34eb6d6c5d0c2d51cd5ee2fc7ee to your computer and use it in GitHub Desktop.
Save vegard/35c5b34eb6d6c5d0c2d51cd5ee2fc7ee to your computer and use it in GitHub Desktop.
Float to byte quantisation
#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;
}
@vegard
Copy link
Author

vegard commented Apr 15, 2020

Output: output

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment