25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more...
Home   Projects   Up   Sitemap   Search   Blog   Forum+Chat   About Us   Privacy   Terms of Use   Feedback   FAQ   Images   Services   Ads   Donate   Humor

IFS fractal. Click for full size.

IFS fractal. Click for full size.

Autorand.cpp - Iterated Function Set fractals

Continuously generates random iterated function sets (IFS) and displays in color the fractal images they define. The program was developed with Borland C++ 4.0 for MSDOS 6, and uses BGI graphics.

A similar program, showfs.cpp, reads the IFS data from a file and displays the associated image.

Both programs are C++ in name only. Their code is really in C. Additional information about them is in their headers below.

There are other versions of these programs (ancestors and descendants) on the site, which you can find by using Search above or Live Search Site below to search for "iterated function".

Most IFS programs seem to use .IFS data files in which the parameters are specified in rectangular form. It is virtually impossible to look at one of these files and determine visually what transformations it defines. I prefer to use a file format in which the parameters are specified in polar form, which I call .PFS files. At the bottom of this page is a program, IFS2PFS.CPP, that can translate an .IFS file to a .PFS file.

IFS fractal. Click for full size.

IFS fractal. Click for full size.

autorand.cpp

/*	autorand.cpp    BORLAND version for MSDOS	5-6-96
	Copyright (C)1996 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License (GPL)
	Version 2 as published by the Free Software Foundation.
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

This program continuously generates random variables for Iterated Function Set (IFS) fractal
images and displays the images using a variety of display options. It is fast. An unusual
feature is the audible warning it gives when an image is nearing completion. The sounds
are triggered by duplicate points being hit.

This was the first conversion from the DeSmet C version (for the Heathkit H100 computer)
to Borland C++ for my "new" PC compatible. It is for MSDOS, using BGI graphics.
It is C++ in name only; most or all of its methods are C.

THIS PROGRAM IS NOW OBSOLETE BECAUSE IT WRITES PROBABILITIES TO FILE AS FLOATS, NOT INTS.
Its files are not compatible with my other IFS fractal generating programs.

It also has been replaced by wshowfs, for Windows.  There's nothing further
left to do here.  Save for archive and reference purposes.

*/
#include <stdio.h>
#include <stddef.h>
#include <dos.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <graphics.h>
#include <time.h>
#include <mem.h>
#include <math.h>
#include <ctype.h>
#include <process.h>
#pragma hdrstop

#include "c:\bcs\my.h"

#define zArray(a)	setmem((a),(sizeof(a)),0)

#include "c:\bcs\mylib.cpp"

double posrnd(void)      // returns 4 digit double from 0. to .9999
{
	return((double)random(10000)/10000.);
}

void main(int argc,char **argv)
{
int i, j;							// temporary
int t;                             	// number of transforms
int k;                             	// transformation choice
int archive = FALSE;               	// flag: whether to automatically archive
									// archive support removed: always false
int beepon = TRUE;                 	// flag: whether to beep near completion
int colorfound;                     // color found at point
int maxcolor;                      	// highest color video driver supports
int dups;                          	// counts duplicated pixels
int duplimit = 1000;                // when reached, image is saved
int dupmargin = 100;               	// beep sounds while dup > limit - margin
int upsdwn = TRUE;                 	// flag: whether to invert y axis
int showequat = TRUE;              	// flag: whether to show equations
int polar = TRUE;                  	// flag: whether to generate ifs by polar
int proportional = FALSE;          	// flag: force x and y linear scaling to equal
int outrange;                      	// flag: whether window limits exceeded
int tempx, tempy;                  	// scaled values to plot
int piccount = 0;                  	// counts number of .PIC files created
int textline;                      	// line numbering for screen text
double a[20], b[20], c[20];        	// transform arrays
double d[20], e[20], f[20];
double p[20];                      	// probabilities
double r;                          	// a random number
double cp[20];                     	// stores probability thresholds
double x, y, newx, newy;      		// numeric values to plot
double minx, maxx, miny, maxy;     	// screen limits
double xrange, yrange;             	// screen ranges
double xfactor, yfactor;      		// scaling factors
double xr[20], ys[20];             	// linear scaling of transforms
double theta[20], phi[20];         	// rotations of transforms, in radians
double thetaangle[20];             	// theta and phi, as angles
double phiangle[20];
long n;                             // counts iterations
long nonpts = 3000;                	// number of points to not display
long throwout = 20;                	// points merely bring near attractor
long pix;                          	// number of pixels turned on
FILE *outfile;
char fnam[81];                     	// for holding input file name
char diskerr[25];                  	// just contains error text
char prbuf[90];                     // text output buffer
char ch;                           	// tests for user keyboard input
char dispmode = 'c';               	// display: <M>ono, <C>olor (by # of hits)
									// or <T>ransform (color by trans. used)

strcpy(diskerr,"File write error.");

printf("\nUsage:  AUTORAND  -options\n");
printf("Example:  autorand  -rca\n");
printf("Options:  r = allow x and y axes to rotate independently\n");
printf("          h = just show this help screen and exit\n");
printf("          b = suppress beep when image (may be) near completion\n");
printf("          d = user will enter # of duplicate points to force .PIC save\n");
printf("          c = set color according to how many times a point gets hit\n");
printf("          t = set color according to which transform was chosen\n");
printf("          m = color mono: green)\n");
printf("          p = make fractal of exactly proportional smaller copies of itself\n");
printf("\nWHILE PROGRAM IS RUNNING:\n");
printf("\nPress <SPACE> to abort & start a new design.\n");
printf("Press B to toggle (enable/disable) beep when near completion\n");
printf("Press C to randomize the screen colors\n");
printf("Press E to display (or re-display) equations.\n");
printf("Press N to display number of points plotted so far.\n");
printf("Press P to save the .PFS data file for the current picture.\n");
printf("Press Q to Quit.\n");
printf("\nTo save screens, run a screen-capture program before starting this one.\n");

if(argc > 1)                          	// just print help & quit
	if(strchr(strlwr(argv[1]),'h'))     // (to lowercase for later tests)
		exit(0);

printf("\nEnter number of transformations: ");
scanf("%d",&t);
randomize();                            // seed random number generator 
if(argc > 1)                            // interpret command line options 
{
	if(strchr(argv[1],'p')) proportional = TRUE;
	if(strchr(argv[1],'c')) dispmode = 'c';
	if(strchr(argv[1],'t')) dispmode = 't';
	if(strchr(argv[1],'m')) dispmode = 'm';
	if(strchr(argv[1],'r')) polar = FALSE;
	if(strchr(argv[1],'b')) beepon = FALSE;
	if(strchr(argv[1],'d'))
	{
		printf("Save .PIC after how many duplicated points?: ");
		scanf("%d",&duplimit);
		dupmargin = min(duplimit/2,100);
	}
}
initgraf();
maxcolor = getmaxcolor();
setcolor(LIGHTBLUE);
setbkcolor(BLACK);
while(1)
{
	if(t > 1)
	{
		clearviewport();
		for(j = 1 ; j < 8 ; j++) // for variety, mix up 7 colors randomly
			setrgbpalette(j,random(256),random(256),random(256));
	}
	zArray(a); zArray(b); zArray(c); zArray(d);  // initialize arrays
	zArray(e); zArray(f); zArray(p); zArray(cp); // to zero
	zArray(xr); zArray(ys); zArray(theta); zArray(phi);
	zArray(thetaangle); zArray(phiangle);

	for(i = 1 ; i <= t ; i++)          		//  generate transform data
	{
		xr[i] = posrnd() * 200. - 100.;     // x axis length scaling factor
		if(proportional)
			ys[i] = xr[i];                  // y axis length scaling factor
		else
			ys[i] = posrnd() * 200. - 100.;
		thetaangle[i] = posrnd() * 360.;    			// x axis rotation,
		theta[i] = thetaangle[i] * 3.1415926 / 180.; 	// to radians
		if(polar)                           // when x and y rotations are
		{                                   // not equal, image "squished"
			phiangle[i] = thetaangle[i];  	// with axes not perpendicular
			phi[i] = theta[i];
		}
		else
		{
			phiangle[i] = posrnd() * 360.;  			// y axis rotation,
			phi[i] = phiangle[i] * 3.1415926 / 180.;    // to radians
		}
		a[i] = xr[i] * cos(theta[i]);      // convert to rectangular
		b[i] = -ys[i] * sin(phi[i]);       // because it's faster
		c[i] = xr[i] * sin(theta[i]);      // for calculations
		d[i] = ys[i] * cos(phi[i]);
		e[i] = posrnd() * 20. - 10.;       // x axis translation distance
		f[i] = posrnd() * 20. - 10.;       // y axis translation distance
		p[i] = 1./t;                       // probability of being picked
	}
	if(showequat)                          // data area on video screen
	{
		sprintf(prbuf,"%d RANDOM TRANSFORMS IN: %d",t,piccount);
		gputs(1,1,LIGHTBLUE,prbuf);
		textline = 1;
	}
	for(i = 1 ; i <= t ; i++)
	{
		if(showequat)                 // polar form
		{
			sprintf(prbuf,"%.0f %.0f %.0f %.0f %.0f %.0f %.2f",
				xr[i],ys[i],thetaangle[i],phiangle[i],e[i],f[i],p[i]);
			gputs(1,++textline,LIGHTBLUE,prbuf);
		}
		a[i] *= .01;  b[i] *= .01;    // adjust to usable form
		c[i] *= .01;  d[i] *= .01;

		cp[i] = cp[i-1] + p[i-1];     // fill probability array
	}
	x = 0. ; y = 0. ;                 // initialize x and y
	n = 0;                            // initialize iteration counter
	dups = 0;                         // initialize duplicate count
	pix = 0;                          // initialize set-pixel count
	ch = '\0';                        // reset user input char
	outrange = 0;                     // reset out of range flag

	if(t == 1)                        // 1­transform requires preset window
	{
		duplimit = 50;
		dupmargin = 25;
		minx = miny = -30.;
		maxx = maxy = 30.;
		xrange = maxx - minx;
		yrange = maxy - miny;
		xfactor = (double)(getmaxx()+1)/xrange;
		yfactor = (double)(getmaxy()+1)/yrange;
	}
	else
	{
		minx = miny = 1.0E+300;       	// init. screen limits
		maxx = maxy = -1.0E+300;      	// for multi-transform sets
	}

	do                  // until runs off screen, user aborts, or completed
	{
		n++;                			// increment iteration counter
		if(kbhit())              		// most passes, just one test, for speed
		{
			ch = tolower(getch());
			if(ch == ' ')            	// user aborts current design
			{
				if(t == 1)
					clearviewport();
				break;
			}
			if(ch == 'c')               // mix up 7 colors randomly
				for(j = 1 ; j < 8 ; j++)
					setrgbpalette(j,random(256),random(256),random(256));

			if(ch == 'b')            // enable/disable beep
				beepon = !beepon;

			if((ch == 'e') || (ch == 'p'))
			{
				sprintf(prbuf,"%d RANDOM TRANSFORMS IN: %d",t,piccount);
				gputs(1,1,LIGHTBLUE,prbuf);
				textline = 1;
				for(i = 1 ; i <= t ; i++)               // polar
				{
					sprintf(prbuf,"%.0f %.0f %.0f %.0f %.0f %.0f %.2f",
						xr[i],ys[i],thetaangle[i],phiangle[i],e[i],f[i],p[i]);
					gputs(1,++textline,LIGHTBLUE,prbuf);
				}
			}

			if((ch == 'n') || (ch == 'e') || (ch == 'p'))
			{
				sprintf(prbuf,"%ld points",n-nonpts);
				gputs(1,t+2,LIGHTBLUE,prbuf);
			}
			if(ch == 'p')
			{
				sprintf(fnam,"%d.PFS",piccount);        // SAVE .PFS FILE
				if((outfile = fopen(fnam,"w")) == NULL)
					aborts(diskerr);
				if(fprintf(outfile,"%d\n",t) == ERR)
					aborts(diskerr);
				for(i = 1 ; i <= t ; i++)     			// polar
					if(fprintf(outfile,"%.0f %.0f %.0f %.0f %.0f %.0f %.2f\n",
						xr[i],ys[i],thetaangle[i],phiangle[i],e[i],f[i],p[i]) == ERR)
							aborts(diskerr);
				if(fclose(outfile) == ERR)
					aborts(diskerr);         
											// picsave went here
				piccount++;
			}
			if(ch == 'q')
			{
				nosound();
				closegraph();
				exit(0);                 	// and exit program
			}
		}                                  	// end if kbhit()

		k = 0;                             	// initialize transformation choice
		do
		{
			r = posrnd();            	// generate random number
		}
		while(r <= 0.);                 // r must be > 0.0 because k must be
										//  incremented at least once
		for(i = 1 ; i <= t ; i++)     	// bump transformation choice up
			if(r > cp[i])            	// depending on where r falls
				k += 1;                 // within the possible distribution

		newx = (a[k])*x + (b[k])*y + e[k];      // calculate new points
		newy = (c[k])*x + (d[k])*y + f[k];

		x = newx ; y = newy;

		if(n <= nonpts)          	// only 1 test most of the time, for speed
		{
			if(t > 1)               // if t==1, window is preset earlier
			{
				if(n <= throwout)   // just loop if working on throwouts
					continue;

				if(n < nonpts)      // tally min and max values seen
				{
					minx = min(minx,x);
					maxx = max(maxx,x);
					miny = min(miny,y);
					maxy = max(maxy,y);
					continue;
				}

				if(n == nonpts)     		// compute screen window size
				{
					xrange = maxx - minx;
					yrange = maxy - miny;

					minx -= (.10 * xrange);       // enlarge window
					maxx += (.10 * xrange);       // by 20%
					miny -= (.10 * yrange);       // in each axis
					maxy += (.10 * yrange);

					xrange = maxx - minx;         // you have to calculate
					yrange = maxy - miny;         // the ranges twice!!

					if(xrange == 0.) xrange = 1.; // prevent div.by zero
					if(yrange == 0.) yrange = 1.;

					xfactor = (double)(getmaxx()+1)/xrange;
					yfactor = (double)(getmaxy()+1)/yrange;

					continue;
				}                                 // end if n == nonpts
			}                                     // end if t > 1
		}                                         // end if n <= nonpts
		tempx = (int)((x-minx)*xfactor);
		tempy = (int)((y-miny)*yfactor);
		if(upsdwn)                                // invert y axis
			tempy = getmaxy() - tempy;

		outrange = FALSE;
		if((tempx < 0) || (tempx > getmaxx()) || (tempy < 0) || (tempy > getmaxy()))
		{
			outrange = TRUE;         // need for after exiting loop
			if(beepon)
				putchar(7);
			break;
		}

		colorfound = getpixel(tempx,tempy);
		if(colorfound == 0)      	// if pixel has not been set at all,
		{                           // reset consecutive dup. count
			dups = 0;           	// to zero (it's a totally new point)
			pix++;                  // and increment pixel-on count
			nosound();              // turn off sound
		}
		else                     	// otherwise, it was a duplicate,
		{
			dups++;                 // so increment dup. counter
			if(beepon)
				if(dups > 300)
					sound(dups);    // turn on speaker
		}
		switch(dispmode)         	// set point, whether or not it was
		{                           // a dup., because it might be in a
			case 'm':               // new color
				putpixel(tempx,tempy,GREEN);
				break;
			case 'c':
				if(++colorfound > maxcolor)
					colorfound = maxcolor;
				putpixel(tempx,tempy,colorfound);
				break;
			case 't':
				putpixel(tempx,tempy,min(k,maxcolor));
				break;
		}
		// could be deleted, since sound() is used above
		if(beepon)
			if(dups > (duplimit - dupmargin))
				putchar(7);

	}
	while(dups < duplimit);
	nosound();

	// falls through if:  ran off screen, was completed, or user aborted
	// save pic below only if picture was completed (and is 'interesting')

	// may want to keep option of autosaving of .pfs files only.
	// however, an overnight run could generate thousands of files

	if(archive)                   // remove this line in final version
		if((ch != ' ') && (!outrange) && (pix > 1500))
		{
			sprintf(fnam,"%d.PFS",piccount);             // SAVE .PFS FILE
			if((outfile = fopen(fnam,"w")) == NULL)
				aborts(diskerr);
			if(fprintf(outfile,"%d\n",t) == ERR)
				aborts(diskerr);
			for(i = 1 ; i <= t ; i++)                         // polar
				if(fprintf(outfile,"%.0f %.0f %.0f %.0f %.0f %.0f %.2f\n",
					xr[i],ys[i],thetaangle[i],phiangle[i],e[i],f[i],p[i]) == ERR)
						aborts(diskerr);
			if(fclose(outfile) == ERR)
				aborts(diskerr);
			piccount++;
		}           // end if picture is interesting, so save it
}                   // end while(1)
}                   // end main()

showfs.cpp

/*	showfs.cpp    BORLAND version for DOS    5-6-96
	Copyright (C)1996 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License (GPL)
	Version 2 as published by the Free Software Foundation.
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

This program is a companion program to autorand.cpp. It reads a data file in either .IFS
format (rectangular coordinates) or .PFS format (polar) and creates the associated
Iterated Function Set (IFS) fractal image. .PFS format makes it much easier to see the
relationships between the numbers and the images produced.

This was the first conversion from the DeSmet C version (for the Heathkit H100 computer)
to Borland C++ for my "new" PC compatible. It is for MSDOS, using BGI graphics.
It is C++ in name only; most or all of its methods are C.

PROGRAM IS NOW OBSOLETE BECAUSE IT WRITES PROBABILITIES TO FILE AS FLOATS, NOT INTS.
Its files are not compatible with my other IFS fractal generating programs.

It has also been replaced by wshowfs for Windows.  There is nothing further
left to do here.  Save for archive and reference purposes.

*/
#include <stdio.h>
#include <stddef.h>
#include <dos.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <graphics.h>
#include <mem.h>
#include <math.h>
#pragma hdrstop

#include "c:\bcs\my.h"

#define zArray(a)	setmem((a),(sizeof(a)),0)

#include "c:\bcs\mylib.cpp"

void main(int argc,char **argv)
{
int i, j;                           // temporary
int t;                              // number of transforms
int k;                              // transformation choice
int color;                          // color used for point
int maxcolor;						// highest color supported by video driver
int polar = FALSE;                 	// whether file is .IFS or .PFS
int upsdwn = TRUE;                 	// flag: whether to invert y axis
int showequat = TRUE;              	// flag: whether to show equations
int tempx, tempy;                  	// actual values to plot
double a[20], b[20], c[20];        	// transform arrays
double d[20], e[20], f[20];
double p[20];                      	// probabilities
double cp[20];                     	// stores probability thresholds
double xr[20], ys[20];              // polar x, y distances
double theta[20], phi[20];          // polar x,y axis rotations, in radians
double thetaangle[20], phiangle[20];// (rotations in degrees)
double r;                          	// a random number
double x, y, newx, newy;      		// screen coordinates
double minx, maxx, miny, maxy;     	// screen limits
double xrange, yrange;             	// screen ranges
double xfactor, yfactor;      		// scaling factors
long n;                             // counts iterations
long nonpts = 2000;                	// number of points to not display
long throwout = 20;                	// points merely bring near attractor
FILE *infile;
char fnam[81];                     	// for holding input file name
char zicnam[81];                   	// input file name, but with .ZED extension
char ch = '\0';                     // tests for user keyboard input
char dispmode = 'c';               	// display: Mono, Color by # hits,
									// color by Transform
char prbuf[90];                     // print buffer for screen output
int grdriver, grmode, errorcode;

//----------------------------------------------------------------------------
// HELP SCREEN
if(argc == 1)                      // user only typed program name
{
	printf("\nUsage:   SHOWFS  [path\\]datafile.ext  -options\n");
	printf("Example: showifs c:\\ifs\\infile.ext  -rn\n");
	printf("         Extension must be .IFS or .PFS\n");
	printf("\nCOMMAND LINE OPTIONS:\n\n");
	printf("e = suppress initial display of ifs variables\n");
	printf("n = user will manually set # of initial unplotted points\n");
	printf("u = invert y axis for upside down image\n");
	printf("c = color determined by number of times a point is hit\n");
	printf("t = color determined by transform chosen for that point\n");
	printf("m = mono: green\n");
	printf("\nWHILE PROGRAM IS RUNNING:\n");
	printf("\nPress C to randomly re-assign color palette\n");
	printf("Press E to display (or re-display) equations.\n");
	printf("Press N to display number of points plotted so far.\n");
	printf("Press P to save screen to a picture file.\n");
	printf("Press Q to Quit.\n");
	exit(0);
}
//----------------------------------------------------------------------------
// DEAL WITH INPUT & OUTPUT FILE NAMES

if(argc > 1)
{
	strcpy(fnam,argv[1]);
	if((infile = fopen(fnam,"rt")) == NULL)
		aborts("Input file not found.");
	strupr(fnam);
}                                           // for use when naming .ZED file
strcpy(zicnam,fnam);                        // copy file name
if(!strcmp(strchr(zicnam,'.'),".PFS"))  	// test for .PFS
	polar = TRUE;
*(strchr(zicnam,'.')) = '\0'; 		// change period to a null (end string)
strcat(zicnam,".PIC");             	// add .ZED extension instead

//----------------------------------------------------------------------------
// INTERPRET COMMAND LINE OPTIONS

if(argc == 3)
{
	if(strchr(strlwr(argv[2]),'c')) dispmode = 'c';   // color by # hits
	if(strchr(strlwr(argv[2]),'t')) dispmode = 't';   // color by transform
	if(strchr(strlwr(argv[2]),'m')) dispmode = 'm';   // mono: green
	if(strchr(strlwr(argv[2]),'u')) upsdwn = FALSE;   // upside down image
	if(strchr(argv[2],'e')) showequat = FALSE;        // don't show numbers
	if(strchr(argv[2],'n'))
	{
		printf("\nEnter number of initial unplotted points: ");
		scanf("%ld",&nonpts);
	}
}

//----------------------------------------------------------------------------
// INITIALIZATIONS

zArray(a); zArray(b); zArray(c); zArray(d);  // initialize arrays
zArray(e); zArray(f); zArray(p); zArray(cp); // to zero 
zArray(xr); zArray(ys); zArray(theta); zArray(phi);
zArray(thetaangle); zArray(phiangle);

randomize();                                 // seed random number generator 

grdriver = DETECT;
grmode = 0;
initgraph(&grdriver,&grmode,"c:\\bc4\\bgi");
errorcode = graphresult();
if(errorcode != 0)
{
	printf("Graphics System Error: %s\n",grapherrormsg(errorcode));
	exit(1);
}
clearviewport();
maxcolor = getmaxcolor();

//----------------------------------------------------------------------------
// READ DISK FILE INFORMATION

fscanf(infile,"%d\n",&t);                    //  get number of transforms
if(polar)                                    //  .PFS FILE
{
	for(i = 1 ; i <= t ; i++)
	{
		fscanf(infile,"%lf %lf %lf %lf %lf %lf %lf\n",
			&xr[i],&ys[i],&thetaangle[i],&phiangle[i],&e[i],&f[i],&p[i]);
		xr[i] *= .01;
		ys[i] *= .01;
		theta[i] = thetaangle[i] * 3.1415926 / 180.;
		phi[i] = phiangle[i] * 3.1415926 / 180.;
		a[i] = xr[i] * cos(theta[i]);            	// convert
		b[i] = -ys[i] * sin(phi[i]);                // to
		c[i] = xr[i] * sin(theta[i]);               // rectangular
		d[i] = ys[i] * cos(phi[i]);
		cp[i] = cp[i-1] + p[i-1];                   // fill probability array
	}
}
else                                         		// .IFS FILE
{
	for(i = 1 ; i <= t ; i++)
	{
		fscanf(infile,"%lf %lf %lf %lf %lf %lf %lf\n",
			&a[i],&b[i],&c[i],&d[i],&e[i],&f[i],&p[i]);
		a[i] *= .01;  b[i] *= .01;                  // adjust to usable form
		c[i] *= .01;  d[i] *= .01;
		cp[i] = cp[i-1] + p[i-1];                   // fill probability array
	}
}
fclose(infile);

//----------------------------------------------------------------------------
//   DATA AREA ON VIDEO SCREEN

if(showequat)
{
	sprintf(prbuf,"%d TRANSFORMS IN: %s",t,fnam);
	outtextxy(0,0,prbuf);
	for(i = 1 ; i <= t ; i++)
	{
		if(polar) 
			sprintf(prbuf,"%.0f %.0f %.0f %.0f %.0f %.0f %.2f",
				xr[i]*100,ys[i]*100,thetaangle[i],phiangle[i],e[i],f[i],p[i]);
		else 
			sprintf(prbuf,"%.0f %.0f %.0f %.0f %.1f %.1f %.2f",
				a[i]*100.,b[i]*100.,c[i]*100.,d[i]*100.,e[i],f[i],p[i]);

		outtextxy(0,i*textheight(prbuf),prbuf);
	}
}

//----------------------------------------------------------------------------
// LAST MINUTE INITIALIZATIONS & 1-TRANSFORM WINDOW SETUP

x = 0. ; y = 0. ;                 // initialize x and y
n = 0;                            // initialize iteration counter
if(t == 1)                        // 1-transform requires preset window
{
	minx = miny = -300.;
	maxx = maxy = 300.;
	xrange = maxx - minx;
	yrange = maxy - miny;
	xfactor = (double)(getmaxx()+1)/xrange;
	yfactor = (double)(getmaxy()+1)/yrange;
}
else
{
	minx = miny = 1.0E+300;       // init. screen limits
	maxx = maxy = -1.0E+300;      // for multi-transform sets
}

//----------------------------------------------------------------------------
// START PROGRAM LOOP

while(1)
{
	n++;                          // increment iteration counter 
	if(kbhit())                   // usually just test once, for speed
	{
		ch = tolower(getch());
		if(ch == 'c')                   // mix up 7 colors randomly 
			for(j = 1 ; j < 8 ; j++) 
				setrgbpalette(j,random(256),random(256),random(256));
		if(ch == 'q')
		{
			closegraph();
			exit(0);
		}
		if(ch == 'e')            		// refresh transform display
		{
			sprintf(prbuf,"%d TRANSFORMS IN: %s",t,fnam);
			outtextxy(0,0,prbuf);
			for(i = 1 ; i <= t ; i++)
			{
				if(polar)
					sprintf(prbuf,"%.0f %.0f %.0f %.0f %.0f %.0f %.2f",
						xr[i]*100,ys[i]*100,thetaangle[i],phiangle[i],e[i],f[i],p[i]);
				else
					sprintf(prbuf,"%.0f %.0f %.0f %.0f %.1f %.1f %.2f",
						a[i]*100.,b[i]*100.,c[i]*100.,d[i]*100.,e[i],f[i],p[i]);
				outtextxy(0,i*textheight(prbuf),prbuf);
			}
		}
		if((ch == 'n') || (ch == 'e'))                 // refresh
		{                                              // point count
			sprintf(prbuf,"%ld points",n-nonpts);
			setfillstyle(EMPTY_FILL,BLACK);
			bar(0,(t+1)*textheight(prbuf),textwidth(prbuf),(t+2)*textheight(prbuf));
			outtextxy(0,(t+1)*textheight(prbuf),prbuf);
		}
		if(ch == 'p')              		// .PIC save routine
			picsave(zicnam);
	}                                  	// END IF USER TYPED A CHARACTER

	k = 0;                             	// INITIALIZE TRANSFORMATION CHOICE
	do
	{
		r = (double)random(10000)/10000.;       // 4 digit rnd >=0., <1.
	}
	while(r <= 0.);               		// r must be > 0 so k gets
										// incremented at least once
	for(i = 1 ; i <= t ; i++)     		// BUMP TRANSFORMATION CHOICE UP
		if(r > cp[i])            		// depending on where r falls
			k += 1;             		// within the possible distribution
											

	newx = (a[k])*x + (b[k])*y + e[k];  // CALCULATE NEW POINTS
	newy = (c[k])*x + (d[k])*y + f[k];

	x = newx ; y = newy;

	if(n <= nonpts)         		// only 1 test most of the time, for speed
	{
		if(t > 1)                	// if t==1, window is preset earlier
		{
			if(n <= throwout)       // just loop if working on throwouts
				continue;

			if(n < nonpts)          // tally min and max values seen
			{
				minx = min(minx,x);
				maxx = max(maxx,x);
				miny = min(miny,y);
				maxy = max(maxy,y);
				continue;
			}

			if(n == nonpts)               		// compute screen window size
			{
				xrange = maxx - minx;
				yrange = maxy - miny;

				minx -= (.1 * xrange);        	// add 20% margin
				maxx += (.1 * xrange);        	// to each axis
				miny -= (.1 * yrange);
				maxy += (.1 * yrange);

				xrange = maxx - minx;         	// you have to calculate
				yrange = maxy - miny;         	// the ranges twice!!

				if(xrange == 0.) xrange = 1.; 	// prevent divide by zero
				if(yrange == 0.) yrange = 1.;

				xfactor = (double)(getmaxx()+1)/xrange;
				yfactor = (double)(getmaxy()+1)/yrange;

				continue;
			}                                  	// end if n == nonpts
		}                                       // end if t > 1
	}                                           // end if n <= nonpts
	tempx = (int)((x-minx)*xfactor );
	tempy = (int)((y-miny)*yfactor );
	if(upsdwn)                                  // invert y axis
		tempy = getmaxy() - tempy;

	switch(dispmode)
	{
		case 'm':                               // monochrome
			putpixel(tempx,tempy,GREEN);
			break;

		case 'c':                               // increment color when hit
			color = getpixel(tempx,tempy);
			if(++color > maxcolor)
				color = maxcolor;
			putpixel(tempx,tempy,color);
			break;

		case 't':                               // color by transform chosen
			putpixel(tempx,tempy,min(k,maxcolor));
			break;
	}
}              // end while(1) point plotting loop
}              // end main()

ifs2pfs.cpp

/*	ifs2pfs.cpp				6-3-99
	Copyright (C)1991-1999 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License (GPL)
	Version 2 as published by the Free Software Foundation.
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

A utility for iterated function set fractal design, this program
reads rectangular data from an .IFS file, translates to polar, and outputs to a .PFS file

This was my final standalone program to do this task; seems to work pretty well, and the
result was incorporated into the wshowfs.cpp program.

------
To do:

It should write negative angles where appropriate instead of 360-x.
They're easier to work with manually.  Or make it an option?

Create an IFS file with transforms that will test this pgm's ability to
correctly translate to PFS for *all* the various special cases:
+/-90 degrees, 0, 360, +/-180, etc.  OR create a random IFS variable generation
routine, and let it run a long time, converting to PFS and back, reporting
only failures.  If it works, it verifies that the
conversion method is reliable, and IFS/PFS are completely interchangeable.

Add a reverse PFS->IFS option (or pgm version) for that purpose, and general use.

------
Notes:
--See notes in paper file on converting IFS back to PFS.
--This version works well.

*/
#include <math.h>
#include "c:\bcs\my.h"
#pragma hdrstop

#include "c:\bcs\mylib.cpp"

//////////////////////////////////////////////////////////////////////////////
// a better version of this should go into mylib.h,
// and keep a watch for places where I currently round incorrectly
// cause rounding to NEAREST int, when truncated.
int round(double d)
{
	if(d > 0.) d += .5;
	if(d < 0.) d -= .5;
	return d;				// truncates decimal portion
}
//////////////////////////////////////////////////////////////////////////////
BOOL closeto(double a, double b)
{
	return (fabs(a - b) < .0001) ? TRUE : FALSE;
}
//////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
string::set_case_sensitive(0);
string::set_paranoid_check(1);

cerr << "Usage:  IFS2PFS infile [outfile]\n";
cerr << "Translates from .IFS (rectangular) to .PFS (polar)\n";
cerr << "If infile extension is omitted, .IFS is assumed.\n";
cerr << "If outfile extension is omitted, .PFS is assumed.\n";
cerr << "If outfile is omitted, infile.PFS is assumed.\n\n";

string infilename = "test.ifs";         // defaults for testing
string outfilename = "test.pfs";

// #error several possibilities not provided for below
if(argc > 1)
{
	infilename = argv[1];
	if(!infilename.contains(".ifs"))
		infilename += ".ifs";
}
ifstream infile(infilename.c_str());
if(!infile)
	aborts("File " + infilename + " not found");

if(argc > 2)
{
	outfilename = argv[2];
	if(!infilename.contains(".pfs"))
		infilename += ".pfs";
}
else
{
	outfilename = infilename;
	outfilename.remove(outfilename.rfind("."));
	outfilename += ".pfs";
}
ofstream outfile(outfilename.c_str(),ios::noreplace);
if(!outfile)
{
	if(yesno("Output file " + outfilename + " already exists.  Overwrite"))
		outfile.open(outfilename.c_str());
	else
		aborts("Correct and rerun.");
}
if(!outfile)
	aborts("File creation error: " + outfilename);

cerr << "Translating " << infilename << " to " << outfilename << endl;

int t;                 					// transform count
double a, b, c, d, e, f, p;         	// rectangular variables
double xr, ys, theta, phi;				// polar variables
char buf[256];

infile >> t;
outfile << t << endl;

BOOL allok = TRUE;					// whether entire set matches original IFS set
for(int i = 0 ; i < t ; i++)
{
	infile >> a >> b >> c >> d >> e >> f >> p;
	if(!infile)
		aborts("Data file " + infilename + " corrupt.  Repair and rerun.");

	// if any of a,b,c,d is in higher range, assume they all are.
	if((fabs(a) > 1) || (fabs(b) > 1) || (fabs(c) > 1) || (fabs(d) > 1))
	{
		a /= 100;
		b /= 100;
		c /= 100;
		d /= 100;
	}

	// probably need to create 4 files: do each of these loops twice, for the
	// + and - sqrts ?  But consider first, and see how well this works.
	//----------------------------------------------------------------------------
	// also see paper notes for additional detail and calcs.
	//
	xr = sqrt(a * a + c * c);
	if(xr == 0)					// both a and c are zero, and theta is unknowable,
		theta = 0;				// so give it a value
	else
		if(c == 0)  			// c is 0, a is NOT, so sin(theta) is 0,
			theta = 0;			// and theta is 0 or 180.
		else
			if(a == 0)			// a is 0, c is NOT, so cos(theta) is 0,
				theta = 90;		// and theta is 90 or 270.
			else				// else theta is something normal, and calculable...
			{
				theta = acos(a / xr) * 180 / M_PI;		// get as degrees
				// there are 2 possibile angles for each acos; the function only returns
				// the one between 0 and 180, and we might need the other.
				// if a/c is positive, cos(theta)/sin(theta) is, also,
				// which tells us which quadrant the angle really should be in.
				switch((int)signum(a / c))
				{
					// a/c is positive, but theta is in quadrant II.
					// flip down to quadrant III
					case 1:
						if((theta >= 90) && (theta < 180))
							theta = 360 - theta;
						break;
					// a/c is negative, but theta is in quadrant I.
					// flip down to quadrant IV
					case -1:
						if((theta > 0) && (theta < 90))
							theta = 360 - theta;
						break;
					default:
						break;
				}

			}
	//----------------------------------------------------------------------------
	ys = sqrt(b * b + d * d);
	if(ys == 0)
		phi = 0;
	else
		if(b == 0)
			phi = 0;
		else
			if(d == 0)
				phi = 90;
			else
			{
				phi = acos(d / ys) * 180 / M_PI;		// get degrees
				switch((int)signum(b / d))
				{
					case -1:
						if((phi >= 90) && (phi < 180))
							phi = 360 - phi;
						break;
					case 1:
						if((phi > 0) && (phi < 90))
							phi = 360 - phi;
						break;
					default:
						break;
				}

			}
	//----------------------------------------------------------------------------
	// see if the numbers we got can reproduce the original set:
	// new rectangular variables
	double a2 = (xr * cosine(theta));
	double b2 = (-ys * sine(phi));
	double c2 = (xr * sine(theta));
	double d2 = (ys * cosine(phi));

	cout << "Transform " << (i + 1) << "     ";
	if(closeto(a,a2) && closeto(b,b2) && closeto(c,c2) && closeto(d,d2))
		cout << "Polar OK: reproduces original rectangular." << endl;
	else
	{
		cout << "Polar NOT OK:" << endl;
		allok = FALSE;
		cout	<< "Old: " << setw(10) << a  << " " << setw(10) << b  << " "
				<< setw(10) << c  << " " << setw(10) << d
				<< " " << e << " " << f << " " << p << endl;
		cout	<< "New: " << setw(10) << a2  << " " << setw(10) << b2  << " "
				<< setw(10) << c2  << " " << setw(10) << d2
				<< " " << e << " " << f << " " << p << endl;
		presskey();
	}

	// writes exact unaltered values as calculated, now usable by wshowfs.
	ostrstream os(buf,sizeof(buf));
	os	<< xr << " " << ys << " " << (theta) << " " << (phi) << " "
		<< e << " " << f << " " << p << endl << ends;

	outfile << buf;

}     		// for(each transform)
// copy over any trailing comments or unused transforms.
string s;
infile >> ws;
s.read_file(infile);
if(s.length())
{
	outfile << endl << s << endl;
	cout << "Comment or unused (and UNtranslated) transform(s) copied to new file:" << endl;
	cout << s << endl;
}
if(allok)
	cout << "It should be safe to delete the IFS file, since the PFS file can replace it."
		 << endl;
else
	cout << "New file does NOT duplicate the old one exactly." << endl;
cout << endl;
presskey();
return(0);
}

 

 

Valid HTML 4.01 Transitional Valid CSS
View content labeling at ICRA.
Copyright ©2007 Steven Whitney. Last modified 09/25/2007.