204 lines
4.6 KiB
C
204 lines
4.6 KiB
C
/**
|
||
* crystal-ng.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-ng.h"
|
||
#include "bmp.h"
|
||
|
||
/* how many colours, max at 256 */
|
||
int colors = 256;
|
||
|
||
/* generate_bitmap() from bmp.c */
|
||
/* extern void line(int x0, int y0, int x1, int y1, unsigned char clr); /\* bres.c *\/ */
|
||
|
||
char new_sprout(int id);
|
||
void process(int x, int y);
|
||
int check_pixel(int x, int y);
|
||
int paint_pixel(int x, int y, unsigned char clr);
|
||
|
||
unsigned char image[HEIGHT][WIDTH][BPP];
|
||
|
||
int main(void)
|
||
{
|
||
if(colors<3) colors = 3;
|
||
if(colors>256) colors = 256;
|
||
|
||
char *file_name = (char*) "output.bmp";
|
||
srand(time(NULL));
|
||
|
||
for(int i=0; i<SPROUTS; ++i)
|
||
{
|
||
char err = new_sprout(i);
|
||
if(err==1) fprintf(stderr, "sprouting failed for id %d\n", i);
|
||
}
|
||
|
||
/* iterate through image grid. if on colored spot, check for empty spots and
|
||
expand as far as possible by velocity */
|
||
for(int x=0; x<WIDTH; ++x)
|
||
{
|
||
printf("%d\n", x);
|
||
for(int y=0; y<HEIGHT; ++y)
|
||
{
|
||
process(x, y);
|
||
}
|
||
}
|
||
|
||
generate_bitmap((unsigned char*)image, HEIGHT, WIDTH, file_name);
|
||
printf("image generation done.\n");
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* color limiting code */
|
||
unsigned char generate_color()
|
||
{
|
||
unsigned char clr = rand() % colors /* + colors */;
|
||
|
||
clr*=(unsigned char)lround( (256/colors) );
|
||
|
||
return clr;
|
||
}
|
||
|
||
char new_sprout(int id)
|
||
{
|
||
if(id >= SPROUTS) return 1;
|
||
|
||
int x = rand() % WIDTH + 1;
|
||
int y = rand() % HEIGHT + 1;
|
||
unsigned char clr = generate_color();
|
||
|
||
printf("sprout %d at (%d|%d)\n", id, x, y);
|
||
paint_pixel(x, y, clr);
|
||
return 0;
|
||
}
|
||
|
||
int order_check(int var, int c, int order)
|
||
{
|
||
if(order==-1)
|
||
return check_pixel(c, var);
|
||
return check_pixel(var, c);
|
||
}
|
||
|
||
/* this function will first check the variables plus the total calculated
|
||
velocity (velocity in this function simply means a value that is added to (var).
|
||
if this does not work, (v) is reduced until it is 0, attempting to find the
|
||
closest uncoloured/empty pixel. once it is found (or is 0), a line is drawn.
|
||
this is how a crystal grows.
|
||
var is a variable value, c is constant value, v is velocity, order is the
|
||
order in which these values are to be checked ( [order_check] ). clr is
|
||
the color. */
|
||
void id_and_proceed(int var, int c, int v, int order, unsigned char clr)
|
||
{
|
||
if(var + v < 0)
|
||
return;
|
||
/* guard */
|
||
/* if(order==-1) /\* y-axis *\/ */
|
||
/* { */
|
||
/* if(var + v < 0) */
|
||
/* return; */
|
||
/* } */
|
||
/* else /\* x-axis *\/ */
|
||
/* { */
|
||
/* } */
|
||
|
||
int r = 2;
|
||
r = order_check(var+v, c, order);
|
||
/* if(order==-1) */
|
||
/* r = check_pixel(c, var); */
|
||
/* else */
|
||
/* r = check_pixel(var, c); */
|
||
|
||
/* if(r==2) return; */
|
||
|
||
/* find most far away empty pixel */
|
||
while(r!=0 && v > 0)
|
||
{
|
||
--v;
|
||
v-=1;
|
||
if(v<0) v=0; /* just in case */
|
||
r=order_check(var+v, c, order);
|
||
/* r=check_pixel(x, y); */
|
||
}
|
||
|
||
do
|
||
{
|
||
if(order==-1)
|
||
paint_pixel(c, var+v, clr);
|
||
else
|
||
paint_pixel(var+v, c, clr);
|
||
--v;
|
||
}
|
||
while(v>=0);
|
||
}
|
||
|
||
void process(int x, int y)
|
||
{
|
||
unsigned char clr = 0;
|
||
|
||
switch(check_pixel(x, y))
|
||
{
|
||
case 0: /* empty/black/background pixel */
|
||
case 2: /* invalid coordinates */
|
||
return;
|
||
case 1: /* colored pixel */
|
||
clr = image[y][x][0];
|
||
break;
|
||
default:
|
||
fprintf(stderr, "process(): this case should never be met.\n");
|
||
return;
|
||
}
|
||
|
||
id_and_proceed(y, x, velocity[0] * (-1), -1, clr); /* north */
|
||
id_and_proceed(x, y, velocity[1], 1, clr); /* east */
|
||
id_and_proceed(y, x, velocity[2], -1, clr); /* south */
|
||
id_and_proceed(x, y, velocity[3] * (-1), 1, clr); /* west */
|
||
|
||
/* int y_north = y - velocity[0]; */
|
||
/* int x_east = x + velocity[1]; */
|
||
/* int y_south = y + velocity[2]; */
|
||
/* int x_west = x - velocity[3]; */
|
||
|
||
return;
|
||
}
|
||
|
||
/* returns 0 if the given pixel is black (0)
|
||
returns 1 if it is coloured
|
||
returns 2 if the coordinates are invalid */
|
||
int check_pixel(int x, 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 2;
|
||
}
|
||
|
||
/* 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(int x, 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;
|
||
}
|