initial commit
This commit is contained in:
460
FIRMWARE/GL_tty.h
Normal file
460
FIRMWARE/GL_tty.h
Normal file
@@ -0,0 +1,460 @@
|
||||
/**
|
||||
* ansi_graphics.h
|
||||
* A couple of function to display graphics in the terminal,
|
||||
* using ansi sequences.
|
||||
* Bruno Levy, Jan 2024
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef GL_FPS
|
||||
#define GL_FPS 30
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(_WIN32) || defined(__APPLE__)
|
||||
#define BIGCPU // we are compiling for a real machine
|
||||
#else
|
||||
#define TINYCPU // we are compiling for a softwore
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h> // for usleep()
|
||||
#endif
|
||||
|
||||
// You can define GL_width and GL_height before
|
||||
// #including ansi_graphics.h in case the plain
|
||||
// old 80x25 pixels does not suffice.
|
||||
|
||||
#ifndef GL_width
|
||||
#define GL_width 80
|
||||
#endif
|
||||
|
||||
#ifndef GL_height
|
||||
#define GL_height 25
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Sets the current graphics position
|
||||
* \param[in] x typically in 0,79
|
||||
* \param[in] y typically in 0,24
|
||||
*/
|
||||
static inline void GL_gotoxy(int x, int y) {
|
||||
printf("\033[%d;%dH",y,x);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the current graphics position
|
||||
* \param[in] R , G , B the RGB color of the pixel, in [0..255]
|
||||
* \details Typically used by programs that draw all pixels sequentially,
|
||||
* like a raytracer. After each line, one can either printf("\n") or
|
||||
* call GL_gotoxy(). If you want to draw individual pixels in an
|
||||
* arbitrary order, use GL_setpixelRGB(x,y,R,G,B)
|
||||
*/
|
||||
static inline void GL_setpixelRGBhere(uint8_t R, uint8_t G, uint8_t B) {
|
||||
// set background color, print space
|
||||
printf("\033[48;2;%d;%d;%dm ",(int)R,(int)G,(int)B);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Draws two "pixels" at the current
|
||||
* cursor position and advances the current cursor
|
||||
* position.
|
||||
* \details Characters are roughly twice as high as wide.
|
||||
* To generate square pixels, this function draws two pixels in
|
||||
* the same character, using the special lower-half white / upper-half
|
||||
* black character, and setting the background and foreground colors.
|
||||
*/
|
||||
static inline void GL_set2pixelsRGBhere(
|
||||
uint8_t r1, uint8_t g1, uint8_t b1,
|
||||
uint8_t r2, uint8_t g2, uint8_t b2
|
||||
) {
|
||||
if((r2 == r1) && (g2 == g1) && (b2 == b1)) {
|
||||
GL_setpixelRGBhere(r1,g1,b1);
|
||||
} else {
|
||||
printf("\033[48;2;%d;%d;%dm",(int)r1,(int)g1,(int)b1);
|
||||
printf("\033[38;2;%d;%d;%dm",(int)r2,(int)g2,(int)b2);
|
||||
// https://www.w3.org/TR/xml-entity-names/025.html
|
||||
// https://onlineunicodetools.com/convert-unicode-to-utf8
|
||||
// https://copypastecharacter.com/
|
||||
printf("\xE2\x96\x83");
|
||||
}
|
||||
}
|
||||
|
||||
#define GL_RGB(R,G,B) #R ";" #G ";" #B
|
||||
|
||||
static inline void GL_setpixelIhere(
|
||||
const char** cmap, int c
|
||||
) {
|
||||
// set background color, print space
|
||||
printf("\033[48;2;%sm ",cmap[c]);
|
||||
}
|
||||
|
||||
static inline void GL_set2pixelsIhere(
|
||||
const char** cmap, int c1, int c2
|
||||
) {
|
||||
if(c1 == c2) {
|
||||
GL_setpixelIhere(cmap, c1);
|
||||
} else {
|
||||
printf("\033[48;2;%sm",cmap[c1]);
|
||||
printf("\033[38;2;%sm",cmap[c2]);
|
||||
// https://www.w3.org/TR/xml-entity-names/025.html
|
||||
// https://onlineunicodetools.com/convert-unicode-to-utf8
|
||||
// https://copypastecharacter.com/
|
||||
printf("\xE2\x96\x83");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Moves the cursor position to the next line.
|
||||
* \details Background and foreground colors are set to black.
|
||||
*/
|
||||
static inline void GL_newline() {
|
||||
printf("\033[38;2;0;0;0m");
|
||||
printf("\033[48;2;0;0;0m\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the color of a pixel
|
||||
* \param[in] x typically in 0,79
|
||||
* \param[in] y typically in 0,24
|
||||
* \param[in] R , G , B the RGB color of the pixel, in [0..255]
|
||||
*/
|
||||
static inline void GL_setpixelRGB(
|
||||
int x, int y, uint8_t R, uint8_t G, uint8_t B
|
||||
) {
|
||||
GL_gotoxy(x,y);
|
||||
GL_setpixelRGBhere(R,G,B);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief restore default foreground and background colors
|
||||
*/
|
||||
static inline void GL_restore_default_colors() {
|
||||
printf(
|
||||
"\033[48;5;16m" // set background color black
|
||||
"\033[38;5;15m" // set foreground color white
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call this function each time graphics should be cleared
|
||||
*/
|
||||
static inline void GL_clear() {
|
||||
GL_restore_default_colors();
|
||||
printf("\033[2J"); // clear screen
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Moves current drawing position to top-left corner
|
||||
* \see GL_setpixelRGBhere() and GL_set2pixelsRGBhere()
|
||||
*/
|
||||
static inline void GL_home() {
|
||||
printf("\033[H");
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call this function before starting drawing graphics
|
||||
* or each time graphics should be cleared
|
||||
*/
|
||||
static inline void GL_init() {
|
||||
printf("\033[?25l"); // hide cursor
|
||||
GL_home();
|
||||
GL_clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Call this function at the end of the program
|
||||
*/
|
||||
static inline void GL_terminate() {
|
||||
GL_restore_default_colors();
|
||||
GL_gotoxy(0,GL_height);
|
||||
printf("\033[?25h"); // show cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Flushes pending graphic operations and waits a bit
|
||||
*/
|
||||
static inline void GL_swapbuffers() {
|
||||
// only flush if we are on a big machine, with true stdio support
|
||||
// otherwise does nothing (because our small MCU io lib is not buffered)
|
||||
#ifdef BIGCPU
|
||||
fflush(stdout);
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
usleep(1000000/GL_FPS);
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef void (*GL_pixelfunc_RGB)(int x, int y, uint8_t* r, uint8_t* g, uint8_t* b);
|
||||
typedef void (*GL_pixelfunc_RGBf)(int x, int y, float* r, float* g, float* b);
|
||||
|
||||
/**
|
||||
* \brief Draws an image by calling a user-specified function for each pixel.
|
||||
* \param[in] width , height dimension of the image in square pixels
|
||||
* \param[in] do_pixel the user function to be called for each pixel
|
||||
* (a "shader"), that determines the (integer) components r,g,b of
|
||||
* the pixel's color.
|
||||
* \details Uses half-charater pixels.
|
||||
*/
|
||||
static inline void GL_scan_RGB(
|
||||
int width, int height, GL_pixelfunc_RGB do_pixel
|
||||
) {
|
||||
uint8_t r1, g1, b1;
|
||||
uint8_t r2, g2, b2;
|
||||
GL_home();
|
||||
for (int j = 0; j<height; j+=2) {
|
||||
for (int i = 0; i<width; i++) {
|
||||
do_pixel(i,j , &r1, &g1, &b1);
|
||||
do_pixel(i,j+1, &r2, &g2, &b2);
|
||||
GL_set2pixelsRGBhere(r1,g1,b1,r2,g2,b2);
|
||||
if(i == width-1) {
|
||||
GL_newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* brief Converts a floating point value to a byte.
|
||||
* \param[in] the floating point value in [0,1]
|
||||
* \return the byte, in [0,255]
|
||||
* \details the input value is clamped to [0,1]
|
||||
*/
|
||||
static inline uint8_t GL_ftoi(float f) {
|
||||
f = (f < 0.0f) ? 0.0f : f;
|
||||
f = (f > 1.0f) ? 1.0f : f;
|
||||
return (uint8_t)(255.0f * f);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Draws an image by calling a user-specified function for each pixel.
|
||||
* \param[in] width , height dimension of the image in square pixels
|
||||
* \param[in] do_pixel the user function to be called for each pixel
|
||||
* (a "shader"), that determines the (floating-point) components
|
||||
* fr,fg,fb of the pixel's color.
|
||||
* \details Uses half-charater pixels.
|
||||
*/
|
||||
static inline void GL_scan_RGBf(
|
||||
int width, int height, GL_pixelfunc_RGBf do_pixel
|
||||
) {
|
||||
float fr1, fg1, fb1;
|
||||
float fr2, fg2, fb2;
|
||||
uint8_t r1, g1, b1;
|
||||
uint8_t r2, g2, b2;
|
||||
GL_home();
|
||||
for (int j = 0; j<height; j+=2) {
|
||||
for (int i = 0; i<width; i++) {
|
||||
do_pixel(i,j , &fr1, &fg1, &fb1);
|
||||
r1 = GL_ftoi(fr1);
|
||||
g1 = GL_ftoi(fg1);
|
||||
b1 = GL_ftoi(fb1);
|
||||
do_pixel(i,j+1, &fr2, &fg2, &fb2);
|
||||
r2 = GL_ftoi(fr2);
|
||||
g2 = GL_ftoi(fg2);
|
||||
b2 = GL_ftoi(fb2);
|
||||
GL_set2pixelsRGBhere(r1,g1,b1,r2,g2,b2);
|
||||
if(i == width-1) {
|
||||
GL_newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
|
||||
#define INSIDE 0
|
||||
#define LEFT 1
|
||||
#define RIGHT 2
|
||||
#define BOTTOM 4
|
||||
#define TOP 8
|
||||
|
||||
#define XMIN 0
|
||||
#define XMAX (GL_width-1)
|
||||
#define YMIN 0
|
||||
#define YMAX (GL_height-1)
|
||||
|
||||
#define code(x,y) \
|
||||
((x) < XMIN) | (((x) > XMAX)<<1) | (((y) < YMIN)<<2) | (((y) > YMAX)<<3)
|
||||
|
||||
/***************************************************************/
|
||||
|
||||
static inline void GL_line(
|
||||
int x1, int y1, int x2, int y2, int R, int G, int B
|
||||
) {
|
||||
int x,y,dx,dy,sx,sy,tmp;
|
||||
|
||||
/* Cohen-Sutherland line clipping. */
|
||||
int code1 = code(x1,y1);
|
||||
int code2 = code(x2,y2);
|
||||
int codeout;
|
||||
|
||||
for(;;) {
|
||||
/* Both points inside. */
|
||||
if(code1 == 0 && code2 == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* No point inside. */
|
||||
if(code1 & code2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* One of the points is outside. */
|
||||
codeout = code1 ? code1 : code2;
|
||||
|
||||
/* Compute intersection. */
|
||||
if (codeout & TOP) {
|
||||
x = x1 + (x2 - x1) * (YMAX - y1) / (y2 - y1);
|
||||
y = YMAX;
|
||||
} else if (codeout & BOTTOM) {
|
||||
x = x1 + (x2 - x1) * (YMIN - y1) / (y2 - y1);
|
||||
y = YMIN;
|
||||
} else if (codeout & RIGHT) {
|
||||
y = y1 + (y2 - y1) * (XMAX - x1) / (x2 - x1);
|
||||
x = XMAX;
|
||||
} else if (codeout & LEFT) {
|
||||
y = y1 + (y2 - y1) * (XMIN - x1) / (x2 - x1);
|
||||
x = XMIN;
|
||||
}
|
||||
|
||||
/* Replace outside point with intersection. */
|
||||
if (codeout == code1) {
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
code1 = code(x1,y1);
|
||||
} else {
|
||||
x2 = x;
|
||||
y2 = y;
|
||||
code2 = code(x2,y2);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap both extremities to ensure x increases
|
||||
if(x2 < x1) {
|
||||
tmp = x2;
|
||||
x2 = x1;
|
||||
x1 = tmp;
|
||||
tmp = y2;
|
||||
y2 = y1;
|
||||
y1 = tmp;
|
||||
}
|
||||
|
||||
// Bresenham line drawing.
|
||||
dy = y2 - y1;
|
||||
sy = 1;
|
||||
if(dy < 0) {
|
||||
sy = -1;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
dx = x2 - x1;
|
||||
|
||||
x = x1;
|
||||
y = y1;
|
||||
|
||||
if(dy > dx) {
|
||||
int ex = (dx << 1) - dy;
|
||||
for(int u=0; u<dy; u++) {
|
||||
GL_setpixelRGB(x,y,R,G,B);
|
||||
y += sy;
|
||||
if(ex >= 0) {
|
||||
x++;
|
||||
ex -= dy << 1;
|
||||
GL_setpixelRGB(x,y,R,G,B);
|
||||
}
|
||||
while(ex >= 0) {
|
||||
x++;
|
||||
ex -= dy << 1;
|
||||
putchar(' ');
|
||||
}
|
||||
ex += dx << 1;
|
||||
}
|
||||
} else {
|
||||
int ey = (dy << 1) - dx;
|
||||
for(int u=0; u<dx; u++) {
|
||||
GL_setpixelRGB(x,y,R,G,B);
|
||||
x++;
|
||||
while(ey >= 0) {
|
||||
y += sy;
|
||||
ey -= dx << 1;
|
||||
GL_setpixelRGB(x,y,R,G,B);
|
||||
}
|
||||
ey += dy << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************/
|
||||
|
||||
#ifdef GL_USE_TURTLE
|
||||
|
||||
#include "sintab.h" // Ugly !!!
|
||||
|
||||
typedef struct {
|
||||
int x; // in [0..79]
|
||||
int y; // in [0..24]
|
||||
int angle; // in degrees
|
||||
int R,G,B; // pen color
|
||||
int pendown; // draw if non-zero
|
||||
} Turtle;
|
||||
|
||||
static inline void Turtle_init(Turtle* T) {
|
||||
T->x = GL_width/2;
|
||||
T->y = GL_height/2;
|
||||
T->angle = -90;
|
||||
T->pendown = 1;
|
||||
T->R = 255;
|
||||
T->G = 255;
|
||||
T->B = 255;
|
||||
}
|
||||
|
||||
static inline void Turtle_pen_up(Turtle* T) {
|
||||
T->pendown = 0;
|
||||
}
|
||||
|
||||
static inline void Turtle_pen_down(Turtle* T) {
|
||||
T->pendown = 1;
|
||||
}
|
||||
|
||||
static inline void Turtle_pen_color(Turtle* T, int R, int G, int B) {
|
||||
T->R = R;
|
||||
T->G = G;
|
||||
T->B = B;
|
||||
}
|
||||
|
||||
static inline void Turtle_forward(Turtle* T, int distance) {
|
||||
int last_x = T->x;
|
||||
int last_y = T->y;
|
||||
int a = T->angle;
|
||||
while(a < 0) {
|
||||
a += 360;
|
||||
}
|
||||
while(a > 360) {
|
||||
a -= 360;
|
||||
}
|
||||
T->x += (costab[a] * distance) / 256;
|
||||
T->y += (sintab[a] * distance) / 256;
|
||||
if(T->pendown) {
|
||||
GL_line(last_x, last_y, T->x, T->y, T->R, T->G, T->B);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Turtle_backward(Turtle* T, int distance) {
|
||||
Turtle_forward(T,-distance);
|
||||
}
|
||||
|
||||
static inline void Turtle_turn_right(Turtle* T, int delta_angle) {
|
||||
T->angle += delta_angle;
|
||||
}
|
||||
|
||||
static inline void Turtle_turn_left(Turtle* T, int delta_angle) {
|
||||
Turtle_turn_right(T, -delta_angle);
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user