221 lines
5.4 KiB
C
221 lines
5.4 KiB
C
/**
|
||
* crystal.c
|
||
*
|
||
* Ⓒ 2023 konomo
|
||
* EUPL-1.2
|
||
**/
|
||
|
||
#include<stdio.h> /* printf; fprintf */
|
||
#include<stdlib.h> /* atoi; rand; srand */
|
||
#include<time.h> /* time */
|
||
#include<math.h> /* lround */
|
||
|
||
#include "crystal.h"
|
||
#include "bmp.h"
|
||
|
||
/* how many colours, max at 256 */
|
||
int colors = 256;
|
||
|
||
extern void generate_bitmap(unsigned char *image, int h, int w, char *file_name); /* bmp.c */
|
||
extern void line(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned char clr); /* bres.c */
|
||
|
||
void paint_picture();
|
||
int paint_pixel(unsigned int x, unsigned int y, unsigned char clr);
|
||
char new_sprout(unsigned int id);
|
||
void process(unsigned int id, unsigned int step);
|
||
|
||
unsigned char image[HEIGHT][WIDTH][BPP];
|
||
struct SPROUT col[SPROUTS]; /* collection of all created sprouts */
|
||
|
||
int main(void)
|
||
{
|
||
if(colors<3) colors = 3;
|
||
if(colors>256) colors=256;
|
||
|
||
char *file_name = (char*) "out.bmp";
|
||
srand(time(NULL));
|
||
|
||
for(unsigned int i=0; i<SPROUTS; ++i)
|
||
{
|
||
char err = new_sprout(i);
|
||
if(err==1) fprintf(stderr, "sprouting failed for id %d\n", i);
|
||
}
|
||
|
||
for(unsigned int i=1; i<=SIMLENGTH; ++i)
|
||
{
|
||
for(unsigned int j=0; j<SPROUTS; ++j) process(j, i);
|
||
}
|
||
|
||
generate_bitmap((unsigned char*)image, HEIGHT, WIDTH, file_name);
|
||
printf("done.\n");
|
||
|
||
return 0;
|
||
}
|
||
|
||
unsigned char generate_color()
|
||
{
|
||
unsigned char clr = rand() % colors + colors;
|
||
|
||
clr*=(unsigned char)lround( (256/colors) );
|
||
|
||
return clr;
|
||
}
|
||
|
||
char new_sprout(unsigned int id)
|
||
{
|
||
if(id>=SPROUTS) return 1;
|
||
|
||
unsigned int sx = rand() % WIDTH + 1;
|
||
unsigned int sy = rand() % HEIGHT + 1;
|
||
/* unsigned int sx = 663; */
|
||
/* unsigned int sy = 17; */
|
||
unsigned char gclr = generate_color(); /* color limiting */
|
||
|
||
unsigned int coords[] = {sx, sy};
|
||
|
||
struct SPROUT s;
|
||
s.ID=id;
|
||
s.c= gclr;
|
||
|
||
for(int i=0; i<4; ++i){ s.v[i]=velocity[i]; }
|
||
|
||
s.x0 = sx;
|
||
s.y0 = sy;
|
||
|
||
col[id]=s;
|
||
|
||
printf("sprout %d at (%d|%d)\n", id, sx, sy);
|
||
paint_pixel(sx, sy, gclr);
|
||
return 0;
|
||
}
|
||
|
||
/* mode is the cardinal direction from north (0) to west (3) */
|
||
unsigned int process_calculation(int mode, unsigned int step, struct SPROUT *s/* , int n */)
|
||
{
|
||
if(mode>3 || s==NULL) return 0;
|
||
|
||
unsigned int velocity = s->v[mode];
|
||
unsigned int deflection = velocity * step;
|
||
unsigned int i = 0;
|
||
|
||
|
||
unsigned int x = s->x0;
|
||
unsigned int y = s->y0;
|
||
unsigned int z[] = {y, x, y, x}; /* this allows a compact switch statement */
|
||
unsigned int limits[] = {0, WIDTH, HEIGHT, 0};
|
||
|
||
switch(mode)
|
||
{
|
||
case 0: /* north y→0 */
|
||
case 3: /* west x→0 */
|
||
if(deflection > z[mode])
|
||
{
|
||
i = 0;
|
||
s->killswitch = 1;
|
||
}
|
||
else
|
||
i = z[mode] - deflection;
|
||
|
||
break;
|
||
case 1: /* east x→infinity */
|
||
case 2: /* south y→infinity */
|
||
i = z[mode] + deflection;
|
||
if ( i > limits[mode] )
|
||
{
|
||
i = limits[mode];
|
||
s->killswitch = 1;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return i;
|
||
}
|
||
|
||
void process(unsigned int id, unsigned int step)
|
||
{
|
||
/* The basic idea of this function is to take a sprout, then calculate the outer
|
||
bounds (4 points) by always calculating the velocity with the current step
|
||
number (step). Then, the Bresenham algorithm (bres.c) is used to draw a line
|
||
between the four boundary points.
|
||
FIRST OF ALL, (id) and (step) need to be checked to their maximal limits
|
||
(SPROUTS) and (SIMLENGTH), respectively, to avoid hopefully impossible problems.
|
||
Throw an error to stderr if they appear so that you know.
|
||
AFTER THAT, you get the sprout through (id), check if it is null, then get the
|
||
(killswitch) variable. If it is 1, then do not proceed, the sprout is done.
|
||
SET (killswitch) TO 1 IF [paint_pixel] returns 1, that is, if the sprout’s
|
||
crystal touches another one. */
|
||
|
||
/* (id) needs to be smaller than (SPROUTS) because they are taken from an
|
||
array which famously start at 0. */
|
||
if(id>=SPROUTS || step>SIMLENGTH)
|
||
{
|
||
fprintf(stderr, "State sprout%d @ %d is bigger than max %d;%d.\n", id, step, SPROUTS, SIMLENGTH);
|
||
return;
|
||
}
|
||
if(col[id].killswitch==1) { /* printf("%d skipped because of killswitch\n", col[id].ID); */ return; }
|
||
|
||
struct SPROUT *s = &col[id];
|
||
|
||
unsigned int north[2], east[2], south[2], west[2];
|
||
|
||
unsigned int rez[] = {0, 0, 0, 0};
|
||
|
||
for(int i=0; i<4; ++i) rez[i] = process_calculation(i, step, s);
|
||
|
||
|
||
north[0] = s->x0;
|
||
north[1] = rez[0];
|
||
|
||
east[0] = rez[1];
|
||
east[1] = s->y0;
|
||
|
||
south[0] = s->x0;
|
||
south[1] = rez[2];
|
||
|
||
west[0] = rez[3];
|
||
west[1] = s->y0;
|
||
|
||
|
||
paint_pixel(s->x0, north[1], s->c);
|
||
paint_pixel(east[0], s->y0, s->c);
|
||
paint_pixel(s->x0, south[1], s->c);
|
||
paint_pixel(west[0], s->y0, s->c);
|
||
|
||
line(west[0], s->y0, s->x0, north[1], s->c);
|
||
line(s->x0, north[1], east[0], s->y0, s->c);
|
||
line(east[0], s->y0, s->x0, south[1], s->c);
|
||
line(s->x0, south[1], west[0], s->y0, s->c);
|
||
|
||
return;
|
||
}
|
||
|
||
int check_pixel(unsigned int x, unsigned int y)
|
||
{
|
||
printf("Checking (%u|%u)\n", x, y);
|
||
if(x>=WIDTH || y>=HEIGHT)
|
||
{
|
||
fprintf(stderr, "coords (%d|%d) exceed image size %d×%d.\n", x, y, WIDTH, HEIGHT);
|
||
return 1;
|
||
}
|
||
|
||
/* as the image will be grayscaled, it works to check only one layer */
|
||
/* this implies the background colour is 0 */
|
||
if(image[y][x][0]!=0)
|
||
{
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int paint_pixel(unsigned int x, unsigned int y, unsigned char clr)
|
||
{
|
||
printf("paint_pixel called\n");
|
||
if(check_pixel(x, y)) return 1;
|
||
|
||
image[y][x][0]=clr;
|
||
image[y][x][1]=clr;
|
||
image[y][x][2]=clr;
|
||
|
||
return 0;
|
||
}
|