intermediate texture preview

This commit is contained in:
Jan Kuhlmann 2024-12-02 17:58:03 +01:00
parent ed5d06d08a
commit 30beea8b75

View File

@ -169,6 +169,30 @@ namespace graphs
bool check = true;
};
// Structure for 24-bit true colors
struct true_color
{
uint8_t red;
uint8_t green;
uint8_t blue;
};
// Using the existing color_type enum for the 4-bit colors (not using std::variant due to its larger size)
using color = union {
color_type col_4;
uint8_t col_8;
struct { // 24-bit true color
uint8_t r;
uint8_t g;
uint8_t b;
};
};
// Intermediate fragment representation potentially holding multiple pixels (e.g. 2x4 as braille)
// most optimal representation possible with only 4 bytes in size, applicable for all types of characters
struct fragment {
color color;
uint8_t data_point_bitfield; // stores up to 8 data points
};
// Number of columns needed to represent the string
// Adapted from: https://stackoverflow.com/a/31124065
inline int strcol(const string &str)
@ -754,6 +778,104 @@ namespace graphs
return 0;
}
// EXPERIMENTAL BEG
template <typename T> // TODO: remove templating?
void histogram_experimental(size_t height, size_t width, double x_min, double x_max, double y_min, double y_max, const T &data, const options &aoptions = {}) {
cout << "Experimental histogram\n";
// precalc graph span
const double x_size = x_max - x_min;
const double y_size = y_max - y_min;
// need width+height of chosen character set (using bar characters as example)
const size_t char_width = 1; // bar supports 1 state per character width
const size_t char_height = 8; // bar supports 8 states per character height
// calc how many data points we can represent with current character set and width/height
const size_t x_points = width * char_width;
const size_t y_points = height * char_height;
// for histograms, every sample needs at least one data point to occupy in x
const size_t x_bar_size = 1; // temporarily fixed to 1
// given the x_bar_size (which should be exposed as an option), see how many points will be placed between bars
const double x_bar_spacer = (double)x_points / ((double)x_bar_size * x_size) - 1;
// histogram specific: each bar needs at least one data point
if ((double)x_points / x_size < 1) {
cerr << "width is too small to fit all histogram bars\n";
return;
}
cout << "width : " << width << '\n';
cout << "height: " << height << '\n';
cout << "x_size: " << x_size << '\n';
cout << "y_size: " << y_size << '\n';
cout << '\n';
cout << "char_width: " << char_width << '\n';
cout << "char_height: " << char_height << '\n';
cout << "x_points: " << x_points << '\n';
cout << "y_points: " << y_points << '\n';
cout << "x_bar_size: " << x_bar_size << '\n';
cout << "x_bar_spacer: " << x_bar_spacer << '\n';
// simple histogram as vector of sample counts
vector<size_t> histogram(x_size, 0);
for (const auto &x: data) {
// check if value is between limits
if (x >= x_min && x < x_max) {
// calculate index on x-axis
const size_t index = x - x_min;
// increment height of the bar
++histogram[index];
}
}
// create 2D array of temporary fragments for the graph
vector<fragment> tex(width * height);
// insert draw histogram data into texture
for (size_t x = 0; x < histogram.size(); x++) {
// calc bar position on x-axis
const size_t x_pos = x * (x_bar_size + x_bar_spacer);
// read bar size from histogram
const size_t y_histo = histogram[x];
// scale to 0-1 range via max bar size
const double y_scaled = (double)y_histo / y_size;
// scale back up via point size
const size_t y_target = y_scaled * (double)y_points;
// calc fragment position and remainder for cap
const size_t y_tex = y_target / char_height;
const size_t y_cap = y_target % char_height;
// draw bar body
for (size_t y = 0; y < y_tex; y++) {
// in texture, y=0 is at the top, so we need to invert the y-axis
const size_t index = x_pos + (height - 1 - y) * width;
tex[index].color.col_4 = aoptions.color;
tex[index].data_point_bitfield = 8; // use full height of character
}
// draw bar cap
if (y_cap > 0) {
// in texture, y=0 is at the top, so we need to invert the y-axis
const size_t index = x_pos + (height - 1 - y_tex) * width;
tex[index].color.col_4 = aoptions.color;
tex[index].data_point_bitfield = y_cap; // use remainder to fill up character
}
}
// draw graph for experimental preview purposes only
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
const size_t index = x + y * width;
const auto& frag = tex[index];
// draw fragment
cout << colors[frag.color.col_4];
cout << bars[frag.data_point_bitfield];
cout << colors[0];
}
cout << '\n';
}
}
// EXPERIMENTAL END
template <typename T>
int histogram(size_t height, size_t width, long double xmin, long double xmax, long double ymin, long double ymax, const T &aarray, const options &aoptions = {})
{