feat: add support for color and buffer shading

This commit is contained in:
dvdrw 2024-11-23 14:08:03 +01:00
parent b5868dfdec
commit c6238f2e1c
Signed by: dvdrw
GPG Key ID: 3ED4E5A371C20DD7
2 changed files with 134 additions and 14 deletions

View File

@ -1,34 +1,61 @@
#pragma once #pragma once
#include "vec.hpp"
#include <cstddef> #include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <cstdio>
#include <functional>
#include <unistd.h> #include <unistd.h>
#include <termios.h> #include <termios.h>
#include <vector>
struct Screen { struct Screen {
char *buf; /**
* Unit pixel information struct
*/
struct Symbol {
char sym;
vec<3> color = {0,0,0};
bool visible = true;
Symbol &operator=(char c) { sym = c; return *this; };
};
/**
* Function taking screen coords and space coords and returning a Symbol for
* that position
*/
using char_shader_t = std::function<Symbol(int, int, float, float)>;
std::size_t w, h; std::size_t w, h;
float dx = 0, dy = 0; float dx = 0, dy = 0;
float sx = 1, sy = 1; float sx = 1, sy = 1.8;
void draw(); void draw();
void clear(); void clear();
char &at(std::size_t n); Symbol &at(std::size_t n);
char &at(std::size_t x, std::size_t y); Symbol &at(int x, int y);
Symbol &at(float x, float y);
void resize(std::size_t n); void resize(std::size_t n);
void resize(std::size_t x, std::size_t y); void resize(std::size_t x, std::size_t y);
std::pair<float, float> screen_to_xy(int w, int h);
std::pair<int, int> xy_to_screen(float x, float y);
Screen(std::size_t n) : buf(nullptr) { resize(n); } Screen(std::size_t n) : buf(nullptr) { resize(n); }
Screen(std::size_t x, std::size_t y) : buf(nullptr) { resize(x, y); } Screen(std::size_t x, std::size_t y) : buf(nullptr) { resize(x, y); }
~Screen() { delete[] buf; } ~Screen() { delete[] buf; }
std::vector<const vec<2>*> points;
char_shader_t shader;
private: private:
char _dummy; Symbol *buf;
Symbol _dummy;
static inline void gotoxy(int x, int y) static inline void gotoxy(int x, int y)
{ {
@ -36,6 +63,19 @@ private:
} }
}; };
using colorizer_t = std::function<vec<3>(float)>;
struct TanhColorizer {
float scale = 1.0;
float translate = 0.0;
vec<3> operator()(float y);
private:
static vec<3> hsl_to_rgb(float h, float s, float l);
static float hue_to_rgb(float p, float q, float t);
};
static termios old, current; static termios old, current;
static inline void enter_noncanonical_mode(void) static inline void enter_noncanonical_mode(void)
{ {

View File

@ -2,33 +2,113 @@
#include <cstring> #include <cstring>
#include <screen.hpp> #include <screen.hpp>
/* Screen */
void Screen::resize(std::size_t n) { void Screen::resize(std::size_t n) {
w = n; h = 1; w = n; h = 1;
buf = (char *)realloc(buf, n * sizeof(char)); buf = (Symbol *)realloc(buf, n * sizeof(Symbol));
} }
void Screen::resize(std::size_t x, std::size_t y) { void Screen::resize(std::size_t x, std::size_t y) {
w = x; h = y; w = x; h = y;
buf = (char *)realloc(buf, x * y * sizeof(char)); buf = (Symbol *)realloc(buf, x * y * sizeof(Symbol));
} }
char &Screen::at(std::size_t n) { return buf[n]; } Screen::Symbol &Screen::at(std::size_t n) { return buf[n]; }
char &Screen::at(std::size_t x, std::size_t y) { Screen::Symbol &Screen::at(int x, int y) {
auto idx = (int)(((y-dy)/sy) * w) + (int)((x-dx)/sx); auto idx = y * w + x;
if(idx >= w *h) return _dummy; if(idx < 0 || idx >= w * h) return _dummy;
return buf[idx]; return buf[idx];
} }
Screen::Symbol &Screen::at(float x, float y) {
auto [a, b] = xy_to_screen(x, y);
if(a < 0 || a >= w || b < 0 || b >= h) return _dummy;
return at(a, b);
}
std::pair<float, float> Screen::screen_to_xy(int x, int y) {
return {
x * sx + dx,
y * sy + dy
};
}
std::pair<int, int> Screen::xy_to_screen(float x, float y) {
return {
(x - dx)/sx,
(y - dy)/sy
};
}
void Screen::clear() { void Screen::clear() {
memset(buf, ' ', w*h); static const Symbol s {' ', {0,0,0}};
for(int i = 0; i < w * h; ++i)
memcpy(buf + i, &s, sizeof(Symbol));
} }
void Screen::draw() { void Screen::draw() {
// "shade" each pixel on screen with #this->shader()
for(int i = 0; i < h; ++i) {
for(int j = 0; j < w; ++j) {
auto [x, y] = screen_to_xy(j, i);
at(j, i) = shader(j, i, x, y);
}
}
// write out a '#' wherever we have a point registered
for(const auto &p : points) {
at(p->x, p->y) = {.sym = '#', .color = 1};
}
// print out each symbol in our buffer to the terminal
gotoxy(0, 0); gotoxy(0, 0);
for(int i = 0; i < h; ++i) { for(int i = 0; i < h; ++i) {
for(int j = 0; j < w; ++j) { for(int j = 0; j < w; ++j) {
std::fputc(at(j, i), stdout); const auto &sym = at(j, i);
const auto color = sym.color.clamp({0,0,0}, {1,1,1}) * 255;
printf("\033[48;2;%d;%d;%dm%c",
(int)color.x, (int)color.y, (int)color.z, sym.sym);
} }
std::fputc('\n', stdout);
// reset color, go to the next line
printf("\033[38;2;%d;%d;%dm\n", 0,0,0);
}
}
/* TanhColorizer */
vec<3> TanhColorizer::operator()(float y) {
y = (std::tanh((y - translate)/scale * 2.25) + 1)/2.f;
float hue = (1.f - y) * 240.f / 360.f;
return hsl_to_rgb(hue, 1, 0.5);
}
vec<3> TanhColorizer::hsl_to_rgb(float h, float s, float l) {
float q = l < 0.5 ? l * (1 + s) : l + s - l * s;
float p = 2 * l - q;
float r = hue_to_rgb(p, q, h + 0.33);
float g = hue_to_rgb(p, q, h);
float b = hue_to_rgb(p, q, h - 0.33);
return { r, g, b };
}
float TanhColorizer::hue_to_rgb(float p, float q, float t) {
if (t < 0) {
t += 1;
} else if (t > 1) {
t -= 1;
}
if (t >= 0.66) {
return p;
} else if (t >= 0.5) {
return p + (q - p) * (0.66 - t) * 6;
} else if (t >= 0.33) {
return q;
} else {
return p + (q - p) * 6 * t;
} }
} }