|
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 |
|
|
Mbrot.cpp - Mandelbrot set displayThis program calculates and displays regions of the Mandelbrot set. It was developed with Borland C++ 4.0 for MSDOS 6, and uses Borland BGI graphics. There are other Mandelbrot display programs (ancestors and descendants of this one) on the site, which you can find by searching this site for: Mandelbrot. |
|
/* mbrot.cpp 4-19-96 1-3-06
Copyright (C)1996, 2006 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
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.
Borland C++ 4.0 version for MSDOS 6. Uses the Borland BGI graphics engine.
Calculates and displays regions of the Mandelbrot set.
Uses my standard .PIC format: width,height\n(x*y chars)
Creates .PIC files allowing 256 colors, but the Borland DOS driver only shows 16, and
recycles through the 16, 16 times.
This is about 1000 times faster than the C version on the H-100 computer.
This DOS version is obsolete now that the Windows version (Winbrot) exists.
This only has one feature the Win version doesn't: sound, which doesn't add anything.
The DOS version RANDOM.INF file is not compatible with the Windows version,
but you can copy the numbers manually.
*/
#include <graphics.h>
#include <complex.h>
#include <dos.h>
#include "c:\bcs\my.h"
#pragma hdrstop
#include "c:\bcs\mylib.cpp"
//////////////////////////////////////////////////////////////////////////////
// global variables
//----------------------------------------------------------------------------
const int MAXITERATIONS = 255; // maximum iterations (and max color index)
int colors[MAXITERATIONS+1]; // array holds color definitions, is read from file
// file structure: color<cr>color<cr>...
// # lines in file = MAXITERATIONS
// *EACH* possible iteration count has an associated color
// colors[MAXITERATIONS-1] (the last line) should be 0 (black)
// so that "mandelbrot" area is black.
BOOL beepon = FALSE; // whether to use sound effects
//----------------------------------------------------------------------------
// fill colors[] array from file
// color map file structure: color<cr>color<cr>...
// # lines in file must be at least MAXITERATIONS
// *EACH* possible iteration count has an associated color
// colors[MAXITERATIONS-1] should be 0 (black)
// Each iteration value maps to a color. The file provided to this function
// should be a text file of 255 lines, with one number on each line.
// Each number is the number of the color to use when plotting the
// iteration count corresponding to that line number in the file.
// Remember that the BGI only has 16 colors available, numbered 0-15.
// EXAMPLE:
// 1 <-- ESCAPE TIME OF 0 MAPS TO COLOR 1
// 4 <-- ESCAPE TIME OF 1 MAPS TO COLOR 4
// 5 <-- ESCAPE TIME OF 2 MAPS TO COLOR 5
// ETC.
// See the end of this page for an example colors file.
void LoadColors()
{
string filename;
cout << endl;
system("dir *.col /b"); // show files available
cout << "\n.COL (color) file to load: (CR for STRIPES.COL): ";
getline(cin,filename,'\n');
if(!filename.length())
filename = "stripes.col";
ifstream infile(filename.c_str());
if(!infile)
aborts("Input file not found.");
for(int i = 0; i <= MAXITERATIONS; i++)
infile >> colors[i];
} //LoadColors
//----------------------------------------------------------------------------
// randomly assign an area to calculate, excluding large blank areas.
// Winbrot.cpp has a much better filtering method.
void randomarea(double& left, double& top, double& right, double& bottom)
{
double vertsize, horizsize, tleft, tright, ttop, tbottom, height, width, aspect;
BOOL goodarea; // whether the chosen area is acceptable
// vertsize = 600, horizsize = 800;
vertsize = 480;
horizsize = 640;
aspect = horizsize / vertsize; // 600/800 = .75
do
{
goodarea = TRUE;
// calculate a random leftmost point
tleft = ((double)random(25001) - 20000.) / 10000.; // -2.0 to +0.50
ttop = ((double)random(25001) - 12500.) / 10000.; // -1.25 to +1.25
do
{
height = (double)random(25000) / 10000.; // a random height 0. to +2.5
}
while(height == 0.0);
width = aspect * height; // set the width
tbottom = ttop - height;
tright = tleft + width;
if((tbottom < -1.25) || (tright > .50))
goodarea = FALSE;
// tests for area including too much of "exclude" areas goes here.
// 8/15/06 It looks like I never incorporated any "boring region tests" in this version.
// The method in Winbrot.cpp is good, but the escape time calculation here (below)
// needs to be pulled out into a function first so it can be called here.
}
while(!goodarea);
left = tleft;
top = ttop;
right = tright;
bottom = tbottom;
} //randomarea
//----------------------------------------------------------------------------
// returns TRUE if another one should be generated, FALSE if not (quit)
BOOL calcmbrot(BOOL userandomarea = FALSE)
{
int i;
int xaspect, yaspect; // for constructing aspect ratio
BOOL doanother = TRUE; // whether to do another one after return
string filename; // base of .INF and .PIC file names
// absolute coordinates within mandelbrot of the area to calculate.
double lowx(-2.0), highx(0.50), lowy(-1.25), highy(1.25); // whole region
int grmode = initgraf();
getaspectratio(&xaspect,&yaspect);
restorecrtmode();
if(userandomarea)
{
filename = "random";
randomarea(lowx,highy,highx,lowy);
}
else
{
cout << "Name of .PIC file to create (omit .PIC): ";
cin >> filename;
cout << "Enter X (REAL) Low : ";
cin >> lowx;
cout << "Enter X (REAL) High : ";
cin >> highx;
cout << "For proper aspect ratio, the Y range (high - low) should be: \n";
cout << (((double)xaspect/(double)yaspect) * (highx - lowx)) << endl;
cout << "Enter Y (IMAGINARY) Low : ";
cin >> lowy;
cout << "Enter Y (IMAGINARY) High: ";
cin >> highy;
}
filename.to_upper();
cout << ".PIC and .INF file names will be: " << filename << endl;
ostrstream os;
os << "X Axis REAL = " << setw(13) << lowx << " " << setw(13) << highx << endl;
os << "Y Axis IMAGINARY = " << setw(13) << lowy << " " << setw(13) << highy << endl;
os << "Aspect ratio = " << ((highy - lowy) / (highx - lowx)) << endl;
os << ends;
clrscr();
cout << os.str();
ofstream outfile((filename + ".inf").c_str()); // save coordinates to the .INF file
outfile << os.str();
outfile.close();
delete[] os.str();
gotoxy(1,25);
cout << "^C = abort";
gotoxy(1,1); // leave cursor here for when pgm ends
setgraphmode(grmode);
int maxx = getmaxx(); // maximum viewport dimensions
int maxy = getmaxy();
// must be binary so every LF isn't expanded to CR LF
outfile.open((filename + ".PIC").c_str(),ios::binary);
outfile << (maxx+1) << "," << (maxy+1) << endl; // binary: \n only writes linefeed
double dx = (highx - lowx) / (double)maxx; // x interval each horiz. pixel
double dy = (highy - lowy) / (double)maxy; // y interval each vert. pixel
complex c(lowx,highy);
for(int row = 0 ; row <= maxy ; row++) // plots in horiz. "stripes"
{
if(kbhit())
{
int ch = tolower(getch());
switch(ch)
{
case 27: // ESC aborts
cout << "Do another (y/n)? ";
return(tolower(getche()) == 'y');
case 'q':
doanother = FALSE; // Quit (after this one is done)
break;
case 'b':
beepon = !beepon;
if(!beepon)
nosound();
break;
}
}
// the math should be compiled to assembler and optimized.
c = complex(lowx,imag(c)); // x-axis back to zero (left side)
for(int column = 0 ; column <= maxx ; column++)
{
// Repeatedly calculate Z = Z*Z + C until the iteration count reaches
// the maximum or until we can be certain it is heading to infinity
complex z(0.,0.);
for(i = 0 ; i < MAXITERATIONS ; i++) // careful: (char)255 + 1 = 0
{
if(abs(z) > 4.0)
break;
z = (z * z) + c;
}
if(beepon)
sound(i * 20);
putpixel(column,row,colors[i]); // show on screen
outfile << (unsigned char)i; // write to file
c += complex(dx,0.); // increment x axis (move right)
}
c -= complex(0.,dy); // decrement y axis (move down)
}
nosound();
picsave("screen.pic");
return(doanother);
} //calcmbrot
//----------------------------------------------------------------------------
// read points from a file and plot to the screen
// This should be the basis for a new version of picload() that allows
// specifying the color map to use.
void loadpicfile()
{
int maxx, maxy;
char ch;
string filename;
cout << endl << "Current directory: " << endl;
system("dir *.pic /b"); // show .pic files available
cout << "\n.PIC file to read (omit .PIC): ";
cin >> filename;
initgraf();
ifstream infile((filename + ".PIC").c_str(),ios::binary);
if(!infile)
aborts("Input file not found.");
infile >> maxx;
infile.ignore(); // the comma
infile >> maxy;
infile.ignore(); // the \n
for(int y = 0 ; y < maxy ; y++)
for(int x = 0 ; x < maxx ; x++)
{
infile.get(ch); // VERY fast
putpixel(x,y,colors[(int)ch]);
}
picsave("screen.pic");
} //loadpicfile
//----------------------------------------------------------------------------
// main()
int main()
{
char ch;
randomize();
clrscr();
do
{
cout << "While running:" << endl;
cout << "ESC Abort immediately" << endl;
cout << "Q Quit after this region is done (RANDOM.INF holds the coordinates)" << endl;
cout << "B Toggle sound" << endl << endl;
cout << "Mbrot" << endl << endl;
cout << "0 Exit" << endl;
cout << "1 Calculate a Mandelbrot region with coordinates you specify" << endl;
cout << "2 Calculate a RANDOM region" << endl;
cout << "3 Display an existing .PIC file." << endl;
cout << "\nEnter choice: ";
ch = getche();
cout << endl;
}
while((ch < '0') || (ch > '3'));
switch(ch)
{
case '1': // user-specified
LoadColors();
calcmbrot();
getch();
break;
case '2': // random
LoadColors();
while(calcmbrot(TRUE));
break;
case '3': // display a file
LoadColors();
loadpicfile();
getch();
break;
}
closegraph();
return(0);
} //main
//////////////////////////////////////////////////////////////////////////////
#if 0
This is an example colors file:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0
#endif // 0
|
|
|
|
|
|