#include <bios.h>
#include <conio.h>
#include <graph.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define max_coord 450

// adjust aspect_factor so that round figures are round and sqaure figures
// are square.	Larger values increase the width of the display.
#define aspect_factor .72

long    current_palette[15];
short   display_color;
short   display_count;
long    display_palette[16];
short   display_rotate;
long    display_timer;
short   display_x1[max_coord];
short   display_x2[max_coord];
short   display_y1[max_coord];
short   display_y2[max_coord];
short   idx_color;
short   line_color[max_coord];
short   max_color;
short   num_color;
short	num_coord;
double	pi;
double	quarter_pi;
double	two_pi;
double  x[252];
double  x_offset;
double  x_size;
double  xy_aspect;
double  x1_coord[max_coord];
double  x2_coord[max_coord];
double  y[252];
double  y_size;
double  y1_coord[max_coord];
double  y2_coord[max_coord];

void    graphics_init(void);
void    draw_curve(void);
void    draw_polygon(void);
void    draw_spiral(void);
void    draw_cardiod(void);
void    draw_dual(void);
void	draw_single(void);
void    set_palette(short);
short   plot_line(double*,double*,double*,double*);
void    advance_color(void);
void    display_graph(short);

void main(void)
	{
	srand((short)time(NULL));
	quarter_pi=atan(1.0);
	pi=4.0*quarter_pi;
	two_pi=8.0*quarter_pi;
	graphics_init();
	while (1)
		{
		switch (rand()%6)
			{
			case 0:
				draw_curve();
				break;
			case 1:
				draw_polygon();
				break;
			case 2:
				draw_spiral();
				break;
			case 3:
				draw_cardiod();
				break;
			case 4:
				draw_single();
				break;
			case 5:
				draw_dual();
				break;
			}
		}
	}

void graphics_init()
	{
	struct videoconfig config;
	_getvideoconfig(&config);
	if (config.monitor&_ANALOG)
		{
		_setvideomode(_VRES16COLOR);
		max_color=15;
		}
	else
		{
		if (config.monitor&_ENHCOLOR)
			{
			_setvideomode(_ERESCOLOR);
			max_color=15;
			}
		else
			{
			if (config.monitor&_COLOR)
				{
				_setvideomode(_HRESBW);
				max_color=1;
				}
			else
				exit(1);
			}
		}
	_getvideoconfig(&config);
	x_size=(double)config.numxpixels-1;
	y_size=(double)config.numypixels-1;
	xy_aspect=aspect_factor*x_size/y_size;
	display_count=0;
	num_coord=0;
	}

void draw_curve()
	{
	register short i,j,k,l,m,n;
	short number_sides,number_points;
	short threads,rotate,tilt,total_points;
	double r_number_sides,r_number_points;
	double r,x1,y1,x2,y2,xd,yd;
	number_sides=rand()%4+3;
	number_points=rand()%(200/number_sides-2)+3;
	if (rand()&1 && number_sides>4)
		threads=2;
	else
		threads=1;
	if (rand()&1)
		{
		set_palette(number_points);
		rotate=1;
		}
	else
		{
		set_palette(number_sides);
		rotate=0;
		}
	if (number_sides&1)
		tilt=2;
	else
		if (number_sides&3)
			tilt=0;
		else
			tilt=1;
	k=0;
	r_number_sides=(double)number_sides;
	r_number_points=(double)number_points;
	for (i=0;i<number_sides;i++)
		{
		r=quarter_pi*(8.0*(double)i/r_number_sides-tilt);
		x1=cos(r);
		y1=sin(r);
		r=quarter_pi*(8.0*(double)(i+1)/r_number_sides-tilt);
		x2=cos(r);
		y2=sin(r);
		xd=(x2-x1)/r_number_points;
		yd=(y2-y1)/r_number_points;
		for (j=0;j<number_points;j++)
			{
			x[k]=x1+(double)j*xd;
			y[k]=y1+(double)j*yd;
			k++;
			}
		}
	total_points=number_sides*number_points;
	if (rotate)
		{
		n=0;
		for (i=0;i<number_points;i++)
			{
			for (j=i;j<total_points;j+=number_points)
				{
				k=j;
				for (m=0;m<threads;m++)
					{
					k+=number_points;
					if (k>=total_points)
						k-=total_points;
					if (plot_line(&x[j],&y[j],&x[k],&y[k]))
						return;
					}
				if (++n==number_sides)
					{
					n=0;
					advance_color();
					}
				}
			}
		}
	else
		{
		k=0;
		for (i=0;i<number_sides;i++)
			{
			for (j=0;j<number_points;j++)
				{
				l=k;
				for (m=0;m<threads;m++)
					{
					l+=number_points;
					if (l>=total_points)
						l-=total_points;
					if (plot_line(&x[k],&y[k],&x[l],&y[l]))
						return;
					}
				k++;
				}
			advance_color();
			}
		}
	display_graph(rotate);
	}

void draw_polygon()
	{
	register short i,j,k,m,n;
	double hp;
	n=rand()%26+5;
	if (n>6 && rand()&1)
		{
		set_palette(n);
		m=num_color>2? 1:0;
		}
	else
		{
		set_palette(n/2);
		m=0;
		}
	hp=8*atan(1.0)/n;
	for (i=0;i<n;i++)
		{
		x[i]=sin(i*hp);
		y[i]=cos(i*hp);
		}
	if (m)
		{
		for (i=0;i<n;i++)
			{
			for (j=i+1;j<=i+n/2;j++)
				{
				k=j>=n? j-n:j;
				if (plot_line(&x[i],&y[i],&x[k],&y[k]))
					return;
				}
			advance_color();
			}
		}
	else
		{
		for (i=1;i<=n/2;i++)
			{
			for (j=0;j<n;j++)
				{
				k=i+j;
				if (k>=n)
					k-=n;
				if (plot_line(&x[j],&y[j],&x[k],&y[k]))
					return;
				}
			advance_color();
			}
		}
	display_graph(m);
	}

void draw_spiral()
	{
	short i,n;
	double delta_angle,accum_angle,accum_dist,x1,y1,x2,y2;
	do
		{
		n=rand()%240+60;
		} while (n==180);
	delta_angle=(double)n*pi/250;
	accum_dist=0.0;
	accum_angle=delta_angle;
	x1=0.0;
	y1=0.0;
	n=max_coord/10*(rand()%9+2);
	set_palette(n);
	for (i=0;i<n;i++)
		{
		accum_dist++;
		x2=x1+accum_dist*cos(accum_angle);
		y2=y1+accum_dist*sin(accum_angle);
		if (plot_line(&x1,&y1,&x2,&y2))
			return;
		accum_angle+=delta_angle;
		if (accum_angle>two_pi)
			{
			accum_angle-=two_pi;
			advance_color();
			}
		y1=y2;
		x1=x2;
		}
	display_graph(1);
	}

void draw_cardiod()
	{
	register short i,j,k,m,n;
	double hp;
	n=rand()%20+6;
	set_palette(n);
	n=10*n+1;
	hp=8*atan(1.0)/n;
	for (i=1;i<=n;i++)
		{
		x[i]=sin(i*hp);
		y[i]=cos(i*hp);
		}
	k=0;
	m=rand()%3+2;
	while (1)
		{
		k++;
		i=k;
		j=m*k;
		while (i>n)
			i-=n;
		while (j>n)
			j-=n;
		if (i==n && j==n)
			break;
		if (plot_line(&x[i],&y[i],&x[j],&y[j]))
			return;
		advance_color();
		}
	if (plot_line(&x[1],&y[1],&x[n-1],&y[n-1]))
		return;
	display_graph(1);
	}

void draw_single()
	{
	register short i,n;
	double max_angle;
	double x1,y1,x2,y2,nx1,nx2;
	n=(rand()%90+11);
	set_palette(n);
	max_angle=(double)(rand()%11+10)/5.0*atan(1.0);
	y1=-cos(max_angle);
	for (i=0;i<=n;i++)
		{
		x1=(double)i/(double)n;
		x2=sin(x1*max_angle);
		y2=-cos(x1*max_angle);
		nx1=-x1;
		nx2=-x2;
		if (plot_line(&nx1,&y1,&x2,&y2))
			return;
		if (plot_line(&x1,&y1,&nx2,&y2))
			return;
		advance_color();
		}
	display_graph(1);
	}

void draw_dual()
	{
	register short i,n;
	double max_angle;
	double x1,y1,x2,y2,nx1,nx2,ny2,zero;
	n=(rand()%90+11);
	set_palette(n);
	max_angle=(double)(rand()%11+10)/5.0*atan(1.0);
	y1=-cos(max_angle);
	zero=0.0;
	for (i=0;i<=n;i++)
		{
		x1=(double)i/(double)n;
		x2=sin(x1*max_angle);
		y2=-cos(x1*max_angle)-y1;
		x1*=2;
		nx1=-x1;
		nx2=-x2;
		ny2=-y2;
		if (plot_line(&nx1,&zero,&x2,&y2))
			return;
		if (plot_line(&x1,&zero,&nx2,&y2))
			return;
		if (plot_line(&nx1,&zero,&x2,&ny2))
			return;
		if (plot_line(&x1,&zero,&nx2,&ny2))
			return;
		advance_color();
		}
	display_graph(1);
	}

void set_palette(req_color)
short req_color;
	{
	register short i;
	short factor_1,factor_2,factor_3,index_1,index_2,index_3;
	double base;
	idx_color=0;
	if (max_color==1)
		{
		current_palette[0]=15;
		num_color=1;
		return;
		}
	switch (rand()%2)
		{
		case 0:
			do
				{
				num_color=rand()%((req_color<1 || req_color>max_color)? max_color:req_color)+1;
				} while (req_color%num_color);
			break;
		case 1:
			for (num_color=15;num_color>0;num_color--)
				if (req_color%num_color==0)
					break;
			break;
		}
	switch (rand()%5)
		{
		case 0:
			base=pi*(double)(rand()%200)/100.0;
			for (i=0;i<num_color;i++)
				{
				index_1=(short)max((short)(63.0*sin(two_pi*(double)i/(double)num_color+base)),0);
				index_2=(short)max((short)(63.0*sin(two_pi*(double)i/(double)num_color+two_pi/3+base)),0);
				index_3=(short)max((short)(63.0*sin(two_pi*(double)i/(double)num_color+4*pi/3+base)),0);
				current_palette[i]=(long)index_1+((long)index_2<<8)+((long)index_3<<16);
				}
			break;
		case 1:
			base=pi*(double)(rand()%200)/100.0;
			for (i=0;i<num_color;i++)
				{
				index_1=(short)(63.0*fabs(sin(two_pi*(double)i/(double)num_color+base)));
				index_2=(short)(63.0*fabs(sin(two_pi*(double)i/(double)num_color+two_pi/3+base)));
				index_3=(short)(63.0*fabs(sin(two_pi*(double)i/(double)num_color+4*pi/3+base)));
				current_palette[i]=(long)index_1+((long)index_2<<8)+((long)index_3<<16);
				}
			break;
		case 2:
			do
				{
				factor_1=rand()%3;
				factor_2=rand()%3;
				} while (factor_1==factor_2);
			factor_3=3-(factor_1|factor_2);
			factor_1*=8;
			factor_2*=8;
			factor_3*=8;
			index_3=rand()%64;
			for (i=0;i<num_color;i++)
				{
				index_1=(short)(63.0*sin(pi*(double)i/(double)num_color));
				index_2=63-index_1;
				current_palette[i]=((long)index_1<<factor_1)+((long)index_2<<factor_2)+((long)index_3<<factor_3);
				}
			break;
		case 3:
			for (i=0;i<num_color;i++)
				{
				do
					{
					index_1=rand()%64;
					index_2=rand()%64;
					index_3=rand()%64;
					} while (index_1<32 && index_2<32 && index_3<32);
				current_palette[i]=(long)index_1+((long)index_2<<8)+((long)index_3<<16);
				}
			break;
		case 4:
			do
				{
				factor_1=rand()%3;
				factor_2=rand()%3;
				factor_3=rand()%3;
				} while (factor_1<2 && factor_2<2 && factor_3<2);
			for (i=0;i<num_color;i++)
				{
				index_1=factor_1*(16*i/num_color+16);
				index_2=factor_2*(16*i/num_color+16);
				index_3=factor_3*(16*i/num_color+16);
				current_palette[i]=(long)index_1+((long)index_2<<8)+((long)index_3<<16);
				}
			break;
		}
	}

void advance_color()
	{
	idx_color++;
	if (idx_color==num_color)
		idx_color=0;
	}

short plot_line(x1,y1,x2,y2)
double *x1,*y1,*x2,*y2;
	{
	if (num_coord==max_coord)
		{
		num_coord=0;
		return(1);
		}
	x1_coord[num_coord]=*x1;
	y1_coord[num_coord]=*y1;
	x2_coord[num_coord]=*x2;
	y2_coord[num_coord]=*y2;
	line_color[num_coord]=idx_color;
	num_coord++;
	return(0);
	}

void display_graph(req_rotate)
short req_rotate;
	{
	register short i,j;
	short rotate_direction;
	long current_timer,ticks,delay_timer;
	double dv;
	if (num_coord==0)
		return;
	dv=1e38;
	for (i=0;i<num_coord;i++)
		if (x1_coord[i]<dv)
			dv=x1_coord[i];
	for (i=0;i<num_coord;i++)
		if (x2_coord[i]<dv)
			dv=x2_coord[i];
	for (i=0;i<num_coord;i++)
		x1_coord[i]-=dv;
	for (i=0;i<num_coord;i++)
		x2_coord[i]-=dv;
	dv=1e38;
	for (i=0;i<num_coord;i++)
		if (y1_coord[i]<dv)
			dv=y1_coord[i];
	for (i=0;i<num_coord;i++)
		if (y2_coord[i]<dv)
			dv=y2_coord[i];
	for (i=0;i<num_coord;i++)
		y1_coord[i]-=dv;
	for (i=0;i<num_coord;i++)
		y2_coord[i]-=dv;
	for (i=0;i<num_coord;i++)
		x1_coord[i]/=x_size/y_size/xy_aspect;
	for (i=0;i<num_coord;i++)
		x2_coord[i]/=x_size/y_size/xy_aspect;
	dv=0.0;
	for (i=0;i<num_coord;i++)
		if (x1_coord[i]>dv)
			dv=x1_coord[i];
	for (i=0;i<num_coord;i++)
		if (x2_coord[i]>dv)
			dv=x2_coord[i];
	for (i=0;i<num_coord;i++)
		if (y1_coord[i]>dv)
			dv=y1_coord[i];
	for (i=0;i<num_coord;i++)
		if (y2_coord[i]>dv)
			dv=y2_coord[i];
	if (dv==0.0)
		return;
	for (i=0;i<num_coord;i++)
		x1_coord[i]*=x_size/dv;
	for (i=0;i<num_coord;i++)
		x2_coord[i]*=x_size/dv;
	for (i=0;i<num_coord;i++)
		y1_coord[i]*=y_size/dv;
	for (i=0;i<num_coord;i++)
		y2_coord[i]*=y_size/dv;
	dv=0.0;
	for (i=0;i<num_coord;i++)
		if (x1_coord[i]>dv)
			dv=x1_coord[i];
	for (i=0;i<num_coord;i++)
		if (x2_coord[i]>dv)
			dv=x2_coord[i];
	dv=(x_size-dv)/2;
	for (i=0;i<num_coord;i++)
		x1_coord[i]+=dv;
	for (i=0;i<num_coord;i++)
		x2_coord[i]+=dv;
	dv=0.0;
	for (i=0;i<num_coord;i++)
		if (y1_coord[i]>dv)
			dv=y1_coord[i];
	for (i=0;i<num_coord;i++)
		if (y2_coord[i]>dv)
			dv=y2_coord[i];
	dv=(y_size-dv)/2;
	for (i=0;i<num_coord;i++)
		y1_coord[i]+=dv;
	for (i=0;i<num_coord;i++)
		y2_coord[i]+=dv;
	if (display_count)
		{
		if (display_rotate && display_color>2)
			rotate_direction=rand()%3;
		else
			rotate_direction=0;
		while (1)
			{
			_bios_timeofday(_TIME_GETCLOCK,&current_timer);
			ticks=current_timer-display_timer;
			if (ticks<0)
				ticks+=1573040;
			if (kbhit())
				{
				while (kbhit())
					getch();
				_setvideomode(_DEFAULTMODE);
				exit(0);
				}
			if (ticks>60)
				break;
			if (ticks>36)
				{
				switch (rotate_direction)
					{
					case 0:
						break;
					case 1:
						display_palette[display_color]=display_palette[0];
						memmove(&display_palette[0],&display_palette[1],display_color*sizeof(display_palette[0]));
						for (i=0;i<display_color;i++)
							_remappalette(i+1,display_palette[i]);
						break;
					case 2:
						memmove(&display_palette[1],&display_palette[0],display_color*sizeof(display_palette[0]));
						display_palette[0]=display_palette[display_color];
						for (i=0;i<display_color;i++)
							_remappalette(i+1,display_palette[i]);
						break;
					}
				}
			do
				{
				_bios_timeofday(_TIME_GETCLOCK,&delay_timer);
				} while (delay_timer==current_timer);
			}
		}
	_setcolor(0);
	switch (rand()%3)
		{
		case 0:
			for (i=0;i<display_count;i++)
				{
				_moveto(display_x1[i],display_y1[i]);
				_lineto(display_x2[i],display_y2[i]);
				}
			break;
		case 1:
			for (i=display_count-1;i>=0;i--)
				{
				_moveto(display_x1[i],display_y1[i]);
				_lineto(display_x2[i],display_y2[i]);
				}
			break;
		case 2:
			for (i=display_count-1;i>=0;i--)
				{
				j=rand()%(i+1);
				_moveto(display_x1[j],display_y1[j]);
				_lineto(display_x2[j],display_y2[j]);
				display_x1[j]=display_x1[i];
				display_x2[j]=display_x2[i];
				display_y1[j]=display_y1[i];
				display_y2[j]=display_y2[i];
				}
			break;
		}
	if (max_color>1)
		for (i=0;i<num_color;i++)
			_remappalette(i+1,current_palette[i]);
	for (i=0;i<num_coord;i++)
		{
		display_x1[i]=(short)x1_coord[i];
		display_x2[i]=(short)x2_coord[i];
		display_y1[i]=(short)y1_coord[i];
		display_y2[i]=(short)y2_coord[i];
		_setcolor(line_color[i]+1);
		_moveto(display_x1[i],display_y1[i]);
		_lineto(display_x2[i],display_y2[i]);
		}
	_bios_timeofday(_TIME_GETCLOCK,&display_timer);
	memcpy(display_palette,current_palette,sizeof(current_palette));
	display_color=num_color;
	display_count=num_coord;
	display_rotate=req_rotate;
	num_coord=0;
	}
