crystal/crystal.c

221 lines
5.4 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 sprouts
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;
}