diff --git a/lavat.c b/lavat.c index 1972428..90890e2 100644 --- a/lavat.c +++ b/lavat.c @@ -1,4 +1,6 @@ #define TB_IMPL +#define TB_LIB_OPTS +#define TB_OPT_TRUECOLOR #include "termbox.h" #include @@ -6,6 +8,7 @@ #include #include #include +#include #define MIN_NBALLS 5 #define MAX_NBALLS 20 @@ -17,6 +20,12 @@ typedef struct { int dy; } Ball; +uintattr_t pallete[11]; +int baseColor[3]; +int baseColor2[3]; +short gradient1=0; +short gradient2=0; + static char *custom = NULL; static char *custom2 = NULL; static short color = TB_WHITE; @@ -26,7 +35,7 @@ static int nballs = 10; static short speedMult = 5; static short rim = 1; static short contained = 0; -static float radiusIn = 100; +static float radiusIn = 110; static float radius; static int margin; static float sumConst; @@ -45,6 +54,45 @@ short next_color(short current); void fix_rim_color(); void set_random_colors(short level); +void set_pallete(); +uintattr_t get_color(float val); +void set_pallete2(); + +int set_color(short *var, int *baseColor, char *optarg, short useGradient) { + if (useGradient) { + // Parse hex color + if (sscanf(optarg, "%02x%02x%02x", &baseColor[0], &baseColor[1], &baseColor[2]) == 3) { + return 1; + } else { + printf("Invalid hex color format. Use format: RRGGBB\n"); + return 0; + } + } else { + // Parse named color + if (strcmp(optarg, "red") == 0) { + *var = TB_RED; + } else if (strcmp(optarg, "yellow") == 0) { + *var = TB_YELLOW; + } else if (strcmp(optarg, "blue") == 0) { + *var = TB_BLUE; + } else if (strcmp(optarg, "green") == 0) { + *var = TB_GREEN; + } else if (strcmp(optarg, "magenta") == 0) { + *var = TB_MAGENTA; + } else if (strcmp(optarg, "cyan") == 0) { + *var = TB_CYAN; + } else if (strcmp(optarg, "black") == 0) { + *var = TB_BLACK; + } else if (strcmp(optarg, "white") == 0) { + *var = TB_WHITE; + } else { + printf("Unknown color: %s\n", optarg); + return 0; + } + return 1; + } +} + int main(int argc, char *argv[]) { if (!parse_options(argc, argv)) @@ -61,6 +109,7 @@ int main(int argc, char *argv[]) { init_params(); + while (1) { // move balls @@ -88,32 +137,48 @@ int main(int argc, char *argv[]) { for (int k = 0; k < nballs; k++) { int y = j * 2 + j2; - sum[j2] += (radius * radius) / - ((float)((i - balls[k].x) * (i - balls[k].x) + - (y - balls[k].y) * (y - balls[k].y))); + float dist_squared = (i - balls[k].x) * (i - balls[k].x) + (y - balls[k].y) * (y - balls[k].y); + if (dist_squared == 0) { + sum[j2] += FLT_MAX; + } else { + sum[j2] += (radius * radius) / dist_squared; + } } } if (!custom) { - if (sum[0] > sumConst) { - if (sum[1] > sumConst) { - tb_printf(i, j, color2, 0, "█"); - } else { - tb_printf(i, j, color2, 0, "▀"); - } - } else if (sum[1] > sumConst) { - tb_printf(i, j, color2, 0, "▄"); - } - - if (rim) { - if (sum[0] > sumConst2) { - if (sum[1] > sumConst2) { - tb_printf(i, j, color, 0, "█"); + if(gradient1){ + if (sum[0] > sumConst) { + if (sum[1] > sumConst) { + tb_printf(i, j, get_color( sum[0]), get_color(sum[1]), "▀"); } else { - tb_printf(i, j, color2, color, "▄"); + tb_printf(i, j, get_color( sum[0]), TB_TRUECOLOR_DEFAULT, "▀"); + } + } else if (sum[1] > sumConst) { + tb_printf(i, j,get_color( sum[1]),TB_TRUECOLOR_DEFAULT, "▄"); + } + + }else{ + if (sum[0] > sumConst) { + if (sum[1] > sumConst) { + tb_printf(i, j, color2, 0, "█"); + } else { + tb_printf(i, j, color2, 0, "▀"); + } + } else if (sum[1] > sumConst) { + tb_printf(i, j, color2, 0, "▄"); + } + + if (rim) { + if (sum[0] > sumConst2) { + if (sum[1] > sumConst2) { + tb_printf(i, j, color, 0, "█"); + } else { + tb_printf(i, j, color2, color, "▄"); + } + } else if (sum[1] > sumConst2) { + tb_printf(i, j, color2, color, "▀"); } - } else if (sum[1] > sumConst2) { - tb_printf(i, j, color2, color, "▀"); } } } else { @@ -188,22 +253,22 @@ void event_handler() { } break; case 'i': - if (radiusIn + 10 <= 150) { - radiusIn += 10; + if (radiusIn + 5 <= 150) { + radiusIn += 5; radius = (radiusIn * radiusIn + (float)(maxX * maxY)) / 15000; margin = contained ? radius * 10 : 0; } break; case 'd': - if (radiusIn - 10 >= 50) { - radiusIn -= 10; + if (radiusIn - 5 >= 100) { + radiusIn -= 5; radius = (radiusIn * radiusIn + (float)(maxX * maxY)) / 15000; margin = contained ? radius * 10 : 0; } break; case 'I': - if (color != TB_WHITE || custom) + if (color != TB_WHITE || custom || gradient1 ) if (rim + 1 <= 5) { rim++; sumConst2 = sumConst * (1 + (float)(0.25 * rim)); @@ -211,7 +276,7 @@ void event_handler() { break; case 'D': - if (color != TB_WHITE || custom) + if (color != TB_WHITE || custom || gradient1) if (rim - 1 >= 0) { rim--; sumConst2 = sumConst * (1 + (float)(0.25 * rim)); @@ -265,6 +330,12 @@ void init_params() { balls[i].dx = (rand() % 2 == 0) ? -1 : 1; balls[i].dy = (rand() % 2 == 0) ? -1 : 1; } + if(gradient1){ + tb_set_output_mode(TB_OUTPUT_TRUECOLOR); + tb_set_clear_attrs(TB_TRUECOLOR_DEFAULT, TB_TRUECOLOR_DEFAULT); + if(gradient2) set_pallete2(); + else set_pallete(); + } } short next_color(short current){ @@ -288,44 +359,36 @@ void set_random_colors( short level){ fix_rim_color(); } -int set_color(short *var, char *optarg) { - - if (strcmp(optarg, "red") == 0) { - *var = TB_RED; - } else if (strcmp(optarg, "yellow") == 0) { - *var = TB_YELLOW; - } else if (strcmp(optarg, "blue") == 0) { - *var = TB_BLUE; - } else if (strcmp(optarg, "green") == 0) { - *var = TB_GREEN; - } else if (strcmp(optarg, "magenta") == 0) { - *var = TB_MAGENTA; - } else if (strcmp(optarg, "cyan") == 0) { - *var = TB_CYAN; - } else if (strcmp(optarg, "black") == 0) { - *var = TB_BLACK; - } else if (strcmp(optarg, "white") == 0) { - *var = TB_WHITE; - } else { - printf("Unknown color: %s\n", optarg); - return 0; - } - return 1; -} - int parse_options(int argc, char *argv[]) { if (argc == 1) return 1; + int c; - while ((c = getopt(argc, argv, ":c:k:s:r:R:b:F:Cp:h")) != -1) { + // First pass to check for gradient mode + optind = 1; // Reset getopt + while ((c = getopt(argc, argv, ":c:k:s:r:R:b:F:Cp:hg")) != -1) { + if (c == 'g') { + gradient1 = 1; + if (!tb_has_truecolor()) { + fprintf(stderr, "The terminal does not support truecolor\n"); + return 0; + } + break; + } + } + + // Reset getopt for second pass + optind = 1; + while ((c = getopt(argc, argv, ":c:k:s:r:R:b:F:Cp:hg")) != -1) { switch (c) { case 'c': - if (!set_color(&color, optarg)) + if (!set_color(&color, baseColor, optarg, gradient1)) return 0; break; case 'k': - if (!set_color(&color2, optarg)) + if (!set_color(&color2, baseColor2, optarg, gradient1)) return 0; + gradient2 = gradient1; // If we're using gradient, enable the second gradient too break; case 's': speedMult = atoi(optarg); @@ -342,18 +405,16 @@ int parse_options(int argc, char *argv[]) { } break; case 'r': - radiusIn = 50 + atoi(optarg) * 10; - if (radiusIn > 150 || radiusIn < 50) { + radiusIn = 100 + atoi(optarg) * 5; + if (radiusIn > 150 || radiusIn < 100) { printf("Invalid radius, only values between 1 and 10 are allowed\n"); return 0; } break; - case 'b': nballs = atoi(optarg); if (nballs > MAX_NBALLS || nballs < MIN_NBALLS) { - - printf("Invalid number of metaballs, only values between %i and %i are" + printf("Invalid number of metaballs, only values between %i and %i are " "allowed\n", MIN_NBALLS, MAX_NBALLS); return 0; @@ -367,7 +428,7 @@ int parse_options(int argc, char *argv[]) { break; case 'p': party = atoi(optarg); - if (party < 0 || party > 3 ) { + if (party < 0 || party > 3) { printf("Invalid party mode, only values between 1 and 3 are allowed\n"); return 0; } @@ -376,6 +437,9 @@ int parse_options(int argc, char *argv[]) { print_help(); return 0; break; + case 'g': + // Already handled in first pass + break; case ':': fprintf(stderr, "Option -%c requires an operand\n", optopt); return 0; @@ -392,8 +456,13 @@ void print_help() { printf( "Usage: lavat [OPTIONS]\n" "OPTIONS:\n" - " -c Set color. Available colours: red, blue, yellow, " - "green, cyan, magenta, white and black. \n" + " -g Enable gradient mode with truecolor support.\n" + " Changes how -c and -k options work.\n" + " -c Set color. In normal mode, available colors are: red, blue, yellow, " + "green, cyan, magenta, white, and black.\n" + " In gradient mode (-g), use hex format: RRGGBB (e.g., FF0000 for red).\n" + " -k Set the rim color. Same format options as -c.\n" + " In gradient mode, this sets the second color for the gradient.\n" " -s Set the speed, from 1 to 10. (default 5)\n" " -r Set the radius of the metaballs, from 1 to 10. " "(default: 5)\n" @@ -403,8 +472,6 @@ void print_help() { "color\n" " If you use Kitty or Alacritty you must use it " "with the -k option to see the rim.\n" - " -k Set the color of the rim if there is one." - " Available colours: red, blue, yellow, green, cyan, magenta, white and black. \n" " -b Set the number of metaballs in the simulation, " "from %i to %i. (default: 10)\n" " -F Allows for a custom set of chars to be used\n" @@ -429,6 +496,58 @@ void print_help() { " p TURN ON THE PARTY AND CYCLE THROUGH THE PARTY MODES " "(it can also turns off the party).\n" "(Tip: Zoom out in your terminal before running the program to get a " - "better resolution of the lava).\n", + "better resolution of the lava).\n" + "EXAMPLES:\n" + " lavat -c green -k red Use named colors in normal mode\n" + " lavat -g -c 00FF00 -k FF0000 Use hex colors in gradient mode\n", MIN_NBALLS, MAX_NBALLS); } + +void set_pallete(){ + int avgColor= (baseColor[0] + baseColor[1] + baseColor[2])/3; + int blackfactor[5]; + int whitefactor[5]; + for(int i=1 ;i<6;i++){ + blackfactor[i-1]=(6-i)*avgColor/5; + whitefactor[i-1]=i*(255-avgColor)/5; + } + + for(int i=0 ;i<5;i++){ + int r, g, b; + float factor = (1 - ((float)blackfactor[i]/(avgColor))); + r = baseColor[0]*factor; + g = baseColor[1]*factor; + b = baseColor[2]*factor; + pallete[i]= (r << 16) | (g << 8) | b; + r = baseColor[0] + (255-baseColor[0])*whitefactor[i]/(255-avgColor); + g = baseColor[1] + (255-baseColor[1])*whitefactor[i]/(255-avgColor); + b = baseColor[2] + (255-baseColor[2])*whitefactor[i]/(255-avgColor); + pallete[i+6] = (r << 16) | (g << 8) | b; + + } + pallete[5]= (baseColor[0] << 16) | (baseColor[1] << 8) | baseColor[2]; +} + +void set_pallete2() { + for (int i = 0; i < 11; i++) { + float t = (float)i / (11 - 1); + + int r = (1 - t) * baseColor[0] + t * baseColor2[0]; + int g = (1 - t) * baseColor[1] + t * baseColor2[1]; + int b = (1 - t) * baseColor[2] + t * baseColor2[2]; + + pallete[i] = (r << 16) | (g << 8) | b; + } +} + +uintattr_t get_color(float val) { + val = (val - sumConst)/(0.001*(rim+1)); + + if (val > 10) { + return pallete[10]; + } else if (val < 0) { + return pallete[0]; + } + + return pallete[(int)val]; +}