|
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 |
|
|
Autorand.cpp - Iterated Function Set fractalsContinuously 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. |
|
/* 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) // 1transform 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 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 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);
}
|
|
|
|
|
|