initial commit

This commit is contained in:
2025-08-02 06:09:31 +03:00
commit 00015ffc03
85 changed files with 62051 additions and 0 deletions

460
FIRMWARE/GL_tty.h Normal file
View 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