lavat/lavat.c

258 lines
6.8 KiB
C
Raw Normal View History

2022-10-17 01:06:46 +00:00
#define TB_IMPL
#include "termbox.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
2022-10-17 13:00:38 +00:00
typedef struct {
2022-10-17 01:06:46 +00:00
int x;
int y;
int dx;
int dy;
} Ball;
2022-10-17 13:00:38 +00:00
static char *custom = NULL;
2022-10-17 01:06:46 +00:00
static short color = TB_DEFAULT;
static int nballs = 10;
static short speedMult = 1;
static short rim = 0;
static short contained = 0;
2022-10-17 01:06:46 +00:00
static float radius = 100;
2022-10-17 01:06:46 +00:00
int parse_options(int argc, char *argv[]);
void print_help();
int main(int argc, char *argv[]) {
if (!parse_options(argc, argv))
return 0;
if (color == TB_DEFAULT && !custom)
2022-10-17 01:06:46 +00:00
rim = 0;
time_t t;
2022-10-17 13:00:38 +00:00
Ball *balls = malloc(sizeof(Ball) * nballs);
2022-10-17 01:06:46 +00:00
struct tb_event event = {0};
srand((unsigned)time(&t));
tb_init();
tb_hide_cursor();
int maxX = tb_width();
int maxY = tb_height() * 2;
int speed = (((1 / (float)(maxX + maxY)) * 5000000) + 50000) / speedMult;
radius = (radius * radius + (float)(maxX * maxY)) / 15000;
2022-10-17 01:06:46 +00:00
2022-10-21 00:48:19 +00:00
int margin = contained ? radius * 10 : 0;
float sumConst = 0.0225;
float sumConst2 = sumConst * (1 + (float)(0.25 * rim));
2022-10-17 01:06:46 +00:00
char *custom2 = custom;
2022-10-18 22:58:06 +00:00
if (custom && strlen(custom) > 1 && rim) {
custom2 = custom + 1;
}
2022-10-17 01:06:46 +00:00
for (int i = 0; i < nballs; i++) {
balls[i].x = rand() % (maxX - 2 * margin) + margin;
balls[i].y = rand() % (maxY - 2 * margin) + margin;
2022-10-17 01:06:46 +00:00
balls[i].dx = (rand() % 2 == 0) ? -1 : 1;
balls[i].dy = (rand() % 2 == 0) ? -1 : 1;
}
while (1) {
// move balls
for (int i = 0; i < nballs; i++) {
if (balls[i].x + balls[i].dx >= maxX - margin ||
balls[i].x + balls[i].dx < margin)
2022-10-17 01:06:46 +00:00
balls[i].dx *= -1;
if (balls[i].y + balls[i].dy >= maxY - margin ||
balls[i].y + balls[i].dy < margin)
2022-10-17 01:06:46 +00:00
balls[i].dy *= -1;
balls[i].x += balls[i].dx;
balls[i].y += balls[i].dy;
}
// render
for (int i = 0; i < maxX; i++) {
for (int j = 0; j < maxY / 2; j++) {
// calculate the two halfs of the block at the same time
float sum[2] = {0};
for (int j2 = 0; j2 < (!custom ? 2 : 1); j2++) {
2022-10-17 01:06:46 +00:00
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)));
2022-10-17 01:06:46 +00:00
}
}
2022-10-17 16:04:09 +00:00
if (!custom) {
if (sum[0] > sumConst) {
if (sum[1] > sumConst) {
2022-10-17 13:00:38 +00:00
tb_printf(i, j, color | TB_BOLD, 0, "");
} else {
tb_printf(i, j, color | TB_BOLD, 0, "");
}
} else if (sum[1] > sumConst) {
2022-10-17 13:00:38 +00:00
tb_printf(i, j, color | TB_BOLD, 0, "");
}
2022-10-17 01:06:46 +00:00
2022-10-17 13:00:38 +00:00
if (rim) {
if (sum[0] > sumConst2) {
if (sum[1] > sumConst2) {
2022-10-17 13:00:38 +00:00
tb_printf(i, j, color, 0, "");
} else {
tb_printf(i, j, color | TB_BOLD, color, "");
}
} else if (sum[1] > sumConst2) {
2022-10-17 13:00:38 +00:00
tb_printf(i, j, color | TB_BOLD, color, "");
}
2022-10-17 01:06:46 +00:00
}
2022-10-17 13:17:02 +00:00
} else {
if (sum[0] > sumConst) {
tb_printf(i, j, color | TB_BOLD, 0, custom2);
2022-10-17 13:00:38 +00:00
}
if (rim) {
if (sum[0] > sumConst2) {
2022-10-17 16:10:04 +00:00
tb_printf(i, j, color, 0, custom);
2022-10-17 01:06:46 +00:00
}
}
}
}
}
tb_present();
usleep(speed);
tb_clear();
tb_peek_event(&event, 10);
if (event.key == TB_KEY_CTRL_C || event.key == TB_KEY_ESC ||
event.ch == 'q' || event.ch == 'Q')
break;
}
tb_shutdown();
free(balls);
}
int parse_options(int argc, char *argv[]) {
if (argc == 1)
return 1;
int c;
while ((c = getopt(argc, argv, ":c:s:r:R:b:F:Ch")) != -1) {
2022-10-17 01:06:46 +00:00
switch (c) {
case 'c':
if (strcmp(optarg, "red") == 0) {
color = TB_RED;
} else if (strcmp(optarg, "yellow") == 0) {
color = TB_YELLOW;
} else if (strcmp(optarg, "blue") == 0) {
color = TB_BLUE;
} else if (strcmp(optarg, "green") == 0) {
color = TB_GREEN;
} else if (strcmp(optarg, "magenta") == 0) {
color = TB_MAGENTA;
} else if (strcmp(optarg, "cyan") == 0) {
color = TB_CYAN;
} else {
printf("Unknown color\n");
return 0;
}
break;
case 's':
speedMult = atoi(optarg);
if (speedMult > 5 || speedMult <= 0) {
printf("Invalid speed, only values between 1 and 5 are allowed\n");
return 0;
}
break;
case 'R':
rim = atoi(optarg);
if (rim > 5 || rim < 1) {
printf("Invalid rim, only values between 1 and 5 are allowed\n");
return 0;
}
break;
case 'r':
2022-10-20 21:39:09 +00:00
radius = 50 + atoi(optarg) * 10;
2022-10-17 01:06:46 +00:00
if (radius > 150 || radius < 50) {
printf("Invalid radius, only values between 1 and 10 are allowed\n");
return 0;
}
break;
case 'b':
nballs = atoi(optarg);
if (nballs > 20 || nballs < 2) {
printf("Invalid number of metaballs, only values between 2 and 20 are"
"allowed\n");
return 0;
}
break;
2022-10-17 13:00:38 +00:00
case 'F':
custom = optarg;
break;
case 'C':
contained = 1;
break;
2022-10-17 01:06:46 +00:00
case 'h':
print_help();
return 0;
break;
case ':': /* -f or -o without operand */
fprintf(stderr, "Option -%c requires an operand\n", optopt);
return 0;
break;
case '?':
fprintf(stderr, "Unrecognized option: -%c\n", optopt);
return 0;
}
}
return 1;
}
void print_help() {
printf(
2022-10-18 03:55:02 +00:00
"Usage: lavat [OPTIONS]\n"
2022-10-17 01:06:46 +00:00
"OPTIONS:\n"
" -c <COLOR> Set color. Available colours: red, blue, yellow, "
2022-10-17 01:06:46 +00:00
"green, cyan and magenta. \n"
" Besides those colors the default one is the normal"
" foreground of your terminal.\n"
" -s <SPEED> Set the speed, from 1 to 5. (default 1)\n"
" -r <RADIUS> Set the radius of the metaballs, from 1 to 10. "
2022-10-17 01:06:46 +00:00
"(default: 5)\n"
" -R <RIM> Set a rim for each metaball, sizes from 1 to 5."
2022-10-17 01:06:46 +00:00
"(default: none)\n"
" This option does not work with the default "
"color.\n"
" -b <NBALLS> Set the number of metaballs in the simulation, "
"from 2 to 20. (default: 10)\n"
" -F <CHARS> Allows for a custom set of chars to be used\n"
" Only ascii symbols are supported for now, "
"wide/unicode chars may appear broken.\n"
" -C Retain the entire lava inside the terminal(It may "
"not "
"work well with a lot of balls or with a bigger radius than the default "
"one).\n"
2022-10-21 00:48:19 +00:00
" -h Print help.\n"
"(Tip: Zoom out in your terminal before running the program to get a "
"better resolution of the lava).\n");
2022-10-17 01:06:46 +00:00
}