-
-
Notifications
You must be signed in to change notification settings - Fork 5
Intermediate representation for layered draw #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
30beea8
366512d
316d210
d57f3c6
1b67cc6
68d18a0
950e339
1d6e7ab
fc29acf
a1057fe
42e9d73
f9ff88c
ebcbc69
353b46c
6cfa086
95b2f7a
cf7b07a
3e66566
fe16c10
e0836b1
afdd9a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
// Teal Dulcet, CS546 | ||
#pragma once | ||
#include <bitset> | ||
#include <cassert> | ||
#include <iostream> | ||
#include <memory> | ||
#include <sstream> | ||
#include <cstring> | ||
#include <cmath> | ||
|
@@ -54,7 +57,7 @@ namespace graphs | |
// {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "} // No border | ||
}; | ||
|
||
enum color_type | ||
enum color_type: uint8_t | ||
{ | ||
color_default, | ||
color_black, | ||
|
@@ -812,6 +815,153 @@ namespace graphs | |
return 0; | ||
} | ||
|
||
// EXPERIMENTAL BEG | ||
|
||
// Using the existing color_type enum for the 4-bit colors (not using std::variant due to its larger size) | ||
union Color { | ||
color_type col_4; // 4-bit color | ||
uint8_t col_8; // 8-bit color | ||
struct { | ||
uint8_t red; | ||
uint8_t green; | ||
uint8_t blue; | ||
} col_24; // 24-bit true color | ||
}; | ||
enum class ColorBits: uint8_t { e4, e8, e24 }; | ||
|
||
// 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; // stores up to 8 data points or up to 255 values | ||
}; | ||
// store fragments on temporary buffer to pass between draws | ||
using Texture = std::vector<Fragment>; | ||
struct Axis { | ||
long double min = 0; | ||
long double max = 0; | ||
bool drawn = true; | ||
bool labels = true; | ||
bool ticks = true; | ||
bool units_label = true; | ||
units_type units = units_fracts; | ||
}; | ||
struct Options { | ||
size_t width = 0; // Width in terminal characters. Set to 0 for automatic size based on terminal. | ||
size_t height = 0; // Height in terminal characters. Set to 0 for automatic size based on terminal. | ||
|
||
Axis x = {}; | ||
Axis y = {}; | ||
|
||
type_type character_set = type_braille; | ||
plot_type plot = plot_scatter; | ||
style_type style = style_light; | ||
graph_type graph = graph_dot; | ||
|
||
std::string title; | ||
std::ostream &ostr = std::cout; | ||
|
||
ColorBits color_type = ColorBits::e4; // bit depth of color representation | ||
bool check = true; // validate sizes for graph draw | ||
bool border = false; // draw border around the graph | ||
bool draw_immediately = true; // draw graph immediately after creation. otherwise call draw/graph with the returned texture | ||
}; | ||
// intermediate representation of a graph texture for ease of passing around | ||
struct Intermediate { | ||
// use a graph texture to draw into the terminal | ||
inline void draw() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am guessing you would want to eventually replace this with the existing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup that's the idea. I didn't wanna taint any functions outside my experimental area yet, so I put it into the Intermediate itself. |
||
// draw graph for experimental preview purposes only | ||
for (size_t y = 0; y < options.height; y++) { | ||
for (size_t x = 0; x < options.width; x++) { | ||
const size_t index = x + y * options.width; | ||
const auto& frag = texture[index]; | ||
// begin color draw( | ||
cout << outputcolor(frag.color.col_4); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably update the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We would need to pass the color bits as well, so the signature could look something like this? inline string outputcolor(Color color, ColorBits color_bits) Makes sense to copy, rather than passing references There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good to me! I usually keep the |
||
// draw character | ||
switch (options.character_set) { | ||
case type_braille: | ||
cout << dots[frag.data]; | ||
break; | ||
case type_block_quadrant: | ||
cout << blocks_quadrant[frag.data]; | ||
break; | ||
default: break; | ||
} | ||
// reset color (TODO: could be optimized to only reset when color changes) | ||
cout << outputcolor(color_type::color_default); | ||
} | ||
cout << '\n'; | ||
} | ||
} | ||
|
||
Texture texture; | ||
const Options options; | ||
}; | ||
|
||
// plot from single data set, drawn on top of existing graph | ||
template <typename T> | ||
void plot_experimental(const T &data, Intermediate &intermediate, Color color = {color_red}) { | ||
cout << "Experimental plot\n"; | ||
|
||
// precalc spans | ||
const Options& options = intermediate.options; | ||
const long double x_span = options.x.max - options.x.min; | ||
const long double y_span = options.y.max - options.y.min; | ||
const long double x_span_recip = 1.0 / x_span; | ||
const long double y_span_recip = 1.0 / y_span; | ||
|
||
// insert draw plot data into texture | ||
for (const auto [x, y]: data) { | ||
// check if value is between limits | ||
if (x >= options.x.min && x < options.x.max && y >= options.y.min && y < options.y.max) { | ||
// calculate terminal character position | ||
const long double x_term = ((long double)x - options.x.min) * x_span_recip * (long double)options.width; | ||
const long double y_term = ((long double)y - options.y.min) * y_span_recip * (long double)options.height; | ||
|
||
// calculate sub-fragment position (2x4 for braille) | ||
const auto [char_width, char_height] = densities[options.character_set]; | ||
size_t x_sub = (x_term - std::floor(x_term)) * char_width; | ||
size_t y_sub = (y_term - std::floor(y_term)) * char_height; | ||
// invert y_sub | ||
y_sub = char_height - 1 - y_sub; | ||
|
||
// draw Fragment | ||
const size_t index = (size_t)x_term + (options.height - 1 - (size_t)y_term) * options.width; | ||
intermediate.texture[index].color = color; // TODO: mix color here | ||
|
||
uint8_t value = 0; | ||
// TODO: put this in separate function to reuse in other plot funcs | ||
switch (options.character_set) { | ||
case type_braille: | ||
value = dotvalues[x_sub][y_sub]; | ||
break; | ||
case type_block_quadrant: | ||
value = 1 << (x_sub + y_sub * char_width); | ||
break; | ||
default: break; | ||
} | ||
intermediate.texture[index].data |= value; | ||
} | ||
} | ||
|
||
// draw plot into terminal immediately if requested | ||
if (options.draw_immediately) { | ||
intermediate.draw(); | ||
} | ||
} | ||
// plot from single data set | ||
template <typename T> | ||
auto plot_experimental(const T &data, const Options &options = {}, Color color = {color_red}) -> Intermediate { | ||
cout << "Experimental plot\n"; | ||
|
||
// create new intermediate object for texture and options | ||
assert(options.width > 0 && options.height > 0); // enforce valid size for now | ||
Intermediate intermediate = { Texture(options.width * options.height), options }; | ||
plot_experimental(data, intermediate, color); | ||
return intermediate; | ||
} | ||
// 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 = {}) | ||
{ | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new
Option
struct is missing some of the existing options. See #9 (comment) above.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was only missing
bool axis
when compared to theOptions
there right? I have addedbool drawn = true
to theAxis
struct for that oneThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are missing the
mark
option as well. Theplot
andgraph
options are currently unused.Interesting about allowing the axes to be enabled independently. That will require some changes to the
graph
function.