                       Chaotic Fractal Screensaver
                          by Magister of Phazix
                      (http://magister.home.ml.org)

------------------------ Contents of this package ----------------------------

This package includes 6 files:

README.TXT   - This readme file.
FRACTAL.ASM  - A source file. This contains a small protected-mode loader.
MAIN.INC     - Another source file. This contains some basic system functions.
FRACTAL.INC  - Another source file. This contains the actual fractal-
                generating code. I warn you, there are almost no comments
                in this, excepting the label names. It has sort of evolved
                over time (and it no doubt has some inefficiencies),
                but I think it still goes pretty fast for what it does.
MAKE.BAT     - A simple batch file that compiles FRACTAL.ASM, MAIN.INC, and
                FRACTAL.INC into FRACTAL.COM.
FRACTAL.COM  - An already compiled version, in case you don't have NASM.
               
The source files are written in NASM assembly. Because the program uses
protected-mode and does not go thru the DPMI, it does not run under
Windows 95 or EMM386.

By the way, last time I checked, you can get NASM at
http://www.programmersheaven.com/files/asm/NASM097.ZIP. They may erase
NASM097.ZIP in the future and replace it with something like NASM098.ZIP.

-------------------------- What is a fractal? --------------------------------

Well, I looked it up, and it wasn't in the dictionary (but my dictionary
wasn't very up to date, either). It seems to mean a sort of shape, which, no
matter how closely looked at, still has rough edges and bumps. With most of
them, you can zoom into a small part of it, and the small part looks vaguely
like a copy of the whole thing.

Two popular fractals are the Mandelbrot set, and the Julia set. Those are
the ones displayed in this program. The Mandelbrot set was named after,
of course, Mr. Mandelbrot, who discovered it. The Julia set, I'm not really
sure but I'd guess that it was probably named after its discoverer too.

------------------ The math behind the Mandelbrot set: -----------------------

Before one can understand the Mandelbrot set, one must understand what an
imaginary number is. An imaginary number is, simply put, the square root
of a negative number.

Remember that a number squared is the number times itself.

----------------->                                           <----------------
Important! ------> Throughout this doc, x^2 means x squared. <------!tnatropmI 
----------------->                                           <----------------

Recall that the square root of a number X is a number Y that, when squared,
yields the original number X. For instance, the square root of 4 is 2,
because 2 squared (2*2) is 4. -2 is also the square root of 4, because -2
squared is 4.

What is the square root of -1. Well, there isn't any _real_ square root
of -1. But mathmaticians decided to pretend there was, and they gave it
a name. It's called "i", the imaginary unit. Consequently, the square root
of -4 is 2i (that's 2 times i).

Note that when you square i (i^2), you get -1. If you take the square root
of any number, and then square it, you end up with the original number. Since
i is the square root of -1, i^2 is -1.

Next, its important to know how to square a binomial. What is a binomial?
It's two numbers added together, like 2x + 6y. Soooo, what is (2x + 6y)^2 ?
Well you could say that's the same as (2x + 6y)*(2x + 6y) and then use
long-multiplication:

   2x  +  6y
*  2x  +  6y
----------------
   4(x^2) + 12xy
+           12xy + 36(y^2)
--------------------------
   4(x^2) + 24xy + 36(y^2)      

There's a shortcut called FOIL, but it's not really too much of a shortcut
and would only complicate the situation.

Now, it is important to know what a complex number is. A complex number is
simply an imaginary number added to a real number. For instance, 2 + 6i
is a complex number. Now what happens when you square a complex number?
Well....

   2  +  6i
*  2  +  6i
---------------
   4  +  12i
+        12i  + 36(i^2)
-------------------------
   4  +  24i  + 36(i^2)

Remember that i^2 is -1. So now our answer is 4 + 24i + 36(-1), which
can be simplified to -32 + 24i. So if you square a complex number, you'll
end up with a complex number.

Right now, it's important to notice that a complex number has two parts:
a real part, and an imaginary part. Any complex number can be written
in the form "a + bi" where a and b are real numbers and i is of course
the imaginary unit, the square root of -1. Because of this, it is
possible to plot a single complex number (you don't need two) on a
2-dimensional graph. You use the real part, a, as the x. Then you use
the imaginary part, b, and the y. In fact, it is very useful to
imagine a complex number as being an ordered pair, a point. The complex
number 2 + 3i is like the point (2,3).

Now, we can go back and find a general formula for the square of a
complex number:

   a  +  bi
*  a  +  bi
--------------
  a^2  + abi
+        abi  + (bi)^2
-----------------------
  a^2  + 2abi + (bi)^2

Which can be simplified to:

a^2 + 2abi + (b^2)(i^2)

And further to:

a^2 + 2abi - b^2

And finally rearranged to

a^2 - b^2 + 2abi

So if we take a complex number a + bi and square it, this will be the result:

Real part of result     : a^2 - b^2
Imaginary part of result: 2ab

Now it's time for the equation of the Mandelbrot set.

# The Mandelbrot set is defined as the set of all complex numbers, C, in #
# which there is NO value p in which Z(p) has a distance greater than 2  #
# from the origin where Z(n) is defined as:                              #
#                                                                        #
# Z (n) = Z(n-1)^2 + C                                                   #
# Z(0) = 0

Well, that's the Mandelbrot set in a nutshell. One other thing, the distance
from the origin of a complex number a+bi is defined the same as the
distance of an ordered pair (a,b) from the origin. The distance of
a+bi from the origin is: square_root(a^2 + b^2). Note that is will always
be a real value.

Also, it's interesting to know that the Mandelbrot set in contained within
the box from (-2,-2) to (2,2) ... or, more accurately, contained within the
circle with the center (0,0) and a radius of 2.

----------------------- I can't understand your code! ------------------------

Oooops! Sorry! That's the way many programs are, especially programs that are
written in assembly language. My program as a bunch of gismos that
over-complicate and bury the important parts of the program.

If you want an algorithm for generating fractals (and not just the assembly
code), then read the sections below ... There are two very simple QBasic
programs for generating the Mandelbrot set and the Julia set.

-------------------- How do I generate the Mandelbrot set --------------------

Here's the definition of the Mandelbrot set again...

# The Mandelbrot set is defined as the set of all complex numbers, C, in #
# which there is NO value p in which Z(p) has a distance greater than 2  #
# from the origin, where Z(n) is defined as:                             #
#                                                                        #
# Z(n) = Z(n-1)^2 + C                                                    #
# Z(0) = 0                                                               #

All right, that may or may not make any sense to you. It is necessary
to understand a little bit of the math, but I'll try to put it in
programmer's, instead of mathmatician's, terms:

** We have a bunch of pixels that we want to color **

That is, of course, the ultimate goal. How do you go about doing it? Well,
you have each pixel on the screen correspond to a particular point on the
Mandelbrot set. So, you need an algorithm that changes a pixel number into a
point on the Mandelbrot set, usually between (-2,-2) and (2,2), because
that's where the whole Mandelbrot set is.

You do it one pixel at a time. You might start in the upper-left hand corner
of the screen, at pixel (0,0). First, you must transform this into a point
on the Mandelbrot set. How about (-2,-2), since that is the upper-left
hand corner of the Mandelbrot set. So now we have the Mandelbrot point
(-2,-2).

** How does that help us color the pixel? **

Well you run the Mandelbrot point (-2,-2) thru a bunch of processing and
eventually you come out with a number that tells how bright to color the
point. Usually, the brightest color means that the point was actually
a part of the Mandelbrot set. Bright colors (but not the brightest) mean
that the point was _almost_ a part of the set. Darker colors mean the
point was not even close. Our point (-2,-2) is actually going to turn out
to be a very dark color. If we tried the point (0,0), however, (which
would be in the center of the screen) we would find that it is actually
part of the Mandelbrot set and would usually be colored the brightest color.
Other points would be in between.

** What is the exact algorithm for determining the color of a point? **

Well, there are many different coloring schemes, but we'll assume this simple
one: Points in the Mandelbrot set are colored white. Points almost in
the set are colored gray. Points far away from the set are colored black.

Well, I'll give you the definition one more time (it'll soak in eventually):

# The Mandelbrot set is defined as the set of all complex numbers, C, in #
# which there is NO value p in which Z(p) has a distance greater than 2  #
# from the origin -- the origin is (0,0) -- where Z(n) is defined as:    #
#                                                                        #
# Z(n) = Z(n-1)^2 + C                                                    #
# Z(0) = 0                                                               #

: We start with our point (-2,-2) and call it C.

: Okay, first we have to find out what Z(0) is. Well, that's easy. It's in
: the definition, it's 0.

: Next, we have to find out what Z(1) is. Ooh, well we'll use this line:
: 
: Z(n) = Z(n-1)^2 + C                                                      
:              
: By plugging 1 into the formula, we get this:
:
: Z(1) = Z(1-1)^2 + C
:
: Which is:
:
: Z(1) = Z(0)^2 + C
:
: Well, we know what Z(0) is, it's zero, so we can plug that in:
:
: Z(1) = 0^2 + C
:
: Zero squared is of course 0, and our C is (-2,-2). Soooo....
:
: Z(1) = (-2,-2)
:
: Our formula says something about the distance from the origin exceeding 2.
: We have to check this value (-2,-2) and see if it's distance from (0,0)
: is greater than 2. To find the distance of a point from the origin, (0,0),
: you use this formula:
:
: Distance = sqrt(x^2 + y^2)
:
: So our distance is: sqrt(4 + 4). That's the square root of 8, which is
: about 2.8. So we have gone more than 2 from the center. At this point,
: we can conclude that this point (-2,-2) is not part of the Mandelbrot
: set. We discovered that when we got to Z(1), which is real early
: (we couldn't have discovered it any sooner), so we'd color this pixel 
: black.

All right. That one was easy. Here's a general algorithm for testing
a point to see if it's in the set:

1) Find Z(0). Is it further than 2 from the center? If so, then quit.
2) Find Z(1). Is it further than 2 from the center? If so, then quit.
3) Find Z(2). Is it further than 2 from the center? If so, then quit.
4) Find Z(3). Is it further than 2 from the center? If so, then quit.
5) Find Z(4). Is it further than 2 from the center? If so, then quit.
6) Find Z(5). Is it further than 2 from the center? If so, then quit.
7) ...

Some points will never become further than 2 from the center. These are
the points that are in the Mandelbrot set. The sooner the point escapes
from the center, the darker you usually color it. Usually, you'll only
check thru Z(100) or so. If a point still hasn't escaped by then, then
you give up and assume that it is part of the Mandelbrot set and that it is
never going to escape. This will cause you to think that certain points
are in the set when they actually are not (as they might escape 2 if you
keep going to 1000), but it will not be noticeable unless you zoom in real
deep into the set. You have to stop somewhere, as there are some points
that will not escape until Z(10000) and even some that will not escape
until Z(1000000). But of course, the number of points that do this are
such an incredibly small percentage that even with a detail of
just 1000, the Mandelbrot set looks pixel-perfect unless you zoom in several
thousand times.

Let's try the point (-1,1)...

# The Mandelbrot set is defined as the set of all complex numbers, C, in #
# which there is NO value p in which Z(p) has a distance greater than 2  #
# from the origin -- the origin is (0,0) -- where Z(n) is defined as:    #
#                                                                        #
# Z(n) = Z(n-1)^2 + C                                                    #
# Z(0) = 0                                                               #

: We start with our point (-1,1) and call it C.

: We find Z(0). It's 0, because it says so in the definition above.

: We find Z(1). Again, we'll use the recursive equation from the definition:
:
: Z(n) = Z(n-1)^2 + C
:
: We plug 1 in for n, since we are trying to find Z(1).
:
: Z(1) = Z(1-1)^2 + C
:
: Which simplifies to:
:
: Z(1) = C
:
: Actually, Z(1) is always going to turn out to be C every time. Our C
: is (-1,1). So that is:
:
: Z(1) = (-1,1)
:
: We can use the distance formula just like last time and discover that
: this point is only about 1.4 from the center. So we are not beyond 2.
: Yuck, that means we have to keep going.

: Now we find Z(2). We plug 2 into the recursive equation and get:
:
: Z(2) = Z(1)^2 + C
:
: Now, remember, we already found Z(1). It's (-1,1). So we'll plug that in:
:
: Z(2) = (-1,1)^2 + C
:
: Uh oh. We have to find the square of (-1,1).
:
: You can read the section above on the math, or you can simply use this
: general formula:
:
: (x,y)^2 = (x^2 - y^2, 2xy)
:
: This only works if we are talking about complex numbers, and we are, so it
: works! So back to the problem. The square of (-1,1) is
:
: ( (-1)^2 - 1^2, 2*(-1)*1 )
:
: Which simplifies to:
:
: (0,-2)
:
: So we found (-1,1)^2. Now we have to go back. We are still trying to find
: Z(2). We know that:
:
: Z(2) = (0,-2) + C
:
: Our C is the one from the beginning of the problem, (-1,1). So we have
:
: Z(2) = (0,-2) + (-1,1)
:
: For addition, you simply add the x's and the y's separately. So we get:
:
: Z(2) = (-1,-1)
:
: Well, now we have to see if the distance exceeded 2. It has not. So
: we keep going.

: Now we need Z(3).
:
: Z(3) = Z(2)^2 + C
:
: We already have Z(2), it is (-1,-1). We square it using the algorithm
: mentioned earlier and we get (0,2). So we have
:
: Z(3) = (0,2) + C
:
: We know C, it's our original value, so we plug it in:
:
: Z(3) = (0,2) + (-1,1)
:
: Now we have this:
:
: Z(3) = (-1,3)
:
: It has a distance of about 3.1 from the origin. That's bigger than 2, so
: our point has escaped. It is not part of the set, but it made it to
: Z(3). It would be darkly colored, but brighter than our other point which
: only made it to Z(1). Other points, as mentioned earlier, aren't going to
: escape until Z(10) or higher. The closer you get to the Mandelbrot set
: itself, the longer it takes to escape. Points that are in the set never
: escape.

You could of course try other points like (0,0) or (-1,0) and discover
that they never escape, or you could try points like (-1,1.5) and see
how long it takes to escape ..... but don't waste to much time. Write a
program and make your processor to it a trillion times faster than you can!

Here is a QBasic (no I don't love QBasic) program which generates the
Mandelbrot set:

' Simple Program To Display The Mandelbrot Set
DEFSNG A-Z
SCREEN 13
Detail = 16

FOR PixelY = 0 TO 200 STEP 1
  FOR PixelX = 0 TO 320 STEP 1
    CX = (PixelX - 160) / 80
    CY = (PixelY - 100) / 50
    ZX = 0
    ZY = 0
    FOR i = 0 TO Detail
      NewZX = ZX * ZX - ZY * ZY + CX
      NewZY = 2 * ZX * ZY + CY
      ZX = NewZX
      ZY = NewZY
      IF (SQR(ZX * ZX + ZY * ZY) > 2) THEN
         PSET (PixelX, PixelY), i
         GOTO NextPoint
      END IF
    NEXT i
    PSET (PixelX, PixelY), 100
NextPoint:
  NEXT PixelX
NEXT PixelY
' It went slow, didn't it!

A major optimization can be made about halfway down, on this line:

'      IF (SQR(ZX * ZX + ZY * ZY) > 2) THEN

That line is the equivalent of this:

'      IF (ZX * ZX + ZY * ZY > 4) THEN

And that cuts out the square root. However, when I made this change, the
QBasic program didn't speed up. The interpreter probably already
automatically made the optimization.

------- Enough about the Mandelbrot set. How do I make the Julia set? --------

The Julia set is not a 2-dimensional set, but a 4-dimensional set (don't
try to picture it) of points. Actually, each point on the Mandelbrot
set corresponds to an 2-dimensional frame of the Julia set. Don't let
this confuse you :-)

Next, here's the definition of the Julia set:

# A "frame", 2-dimensional plane, given by C, of the Julia set is the    #
# set of all points Z(0) in which there is no value p in which Z(p) has  #
# a distance greater than 2 from the origin -- (0,0) -- where Z(n) is:   #
#                                                                        #
# Z(n) = Z(n-1)^2 + C                                                    #

The Julia set, like the Mandelbrot set, is contained within the box from
(-2,-2) to (2,2) ... or, more accurately, contained within the circle with
the center (0,0) and radius of 2.

So, from a programmer's point of view, if you want to render a frame of
the Julia set, what do you do? Well, first, you pick a value for C. This
determines what the whole frame looks like. It is wise to pick a value
between (-2,-2) and (2,2) or your frame will look very dull. You keep
the value C the same for every pixel on the frame. Now, you'll do it one
pixel at a time. Suppose your at the pixel in the upper-left hand
corner: (0,0).

Since that is in the upper-left hand corner of the screen, you'll likely
make that correspond to the point (-2,-2) of the Julia set. That (-2,-2)
is the value for Z(0). From there, you use the exact same code to render
the Julia set, as you did the Mandelbrot set. Actually, it is very easy
to take a program that displays the Mandelbrot set and change it to display
tho Julia set. These are the main differences between the Julia set and
the Mandelbrot set:

1) They look different.
2) The Mandelbrot set alway has a value of (0,0) for Z(0).
   The Mandelbrot set varies the value for C as the pixel on the screen
   changes.
3) The Julia set varies both the values C and Z(0). C is usually used
   for the frame while Z(0) is usually used for the pixel.
   Note: If you do it the other way around (Z(0) used for frame and C used f
   or pixel), you'll get this Julia-like Mandelbrot set which sort of falls
   apart as the frame changes. It doesn't really look that nice.
    
One other interesting point about the Julia set. Remember that the value
of C determines what frame of the Julia set is displayed. If the point C
is a part of the Mandelbrot set, then the Julia set will all be connected.
It will be in one piece. If the point C is not in the Mandelbrot set, then
the Julia set will be disconnected. There will be gaps between parts of
the Julia set. The Julia set is most interesting near the edges of the
Mandelbrot set. The Julia set with C at (0,0) is a boring looking round ball.
Don't pick values of C that are deep inside the center of the Mandelbrot set.

Here is the QBasic program to display a frame of the Julia set (you can
change the value of CX and CY to change the frame):

' Simple Program To Display The Julia Set
DEFSNG A-Z
SCREEN 13
Detail = 16
CX = -.25
CY = -.75
FOR PixelY = 0 TO 200 STEP 1
  FOR PixelX = 0 TO 320 STEP 1
    ZX = (PixelX - 160) / 80
    ZY = (PixelY - 100) / 50
    FOR i = 0 TO Detail
      NewZX = ZX * ZX - ZY * ZY + CX
      NewZY = 2 * ZX * ZY + CY
      ZX = NewZX
      ZY = NewZY
      IF (ZX * ZX + ZY * ZY > 4) THEN
         PSET (PixelX, PixelY), i
         GOTO NextPoint
      END IF
    NEXT i
    PSET (PixelX, PixelY), 100
NextPoint:
  NEXT PixelX
NEXT PixelY

Notice that the code between these lines ---

    FOR i = 0 TO Detail
    NEXT i

--- is exactly the same as the Mandelbrot set generator.

The end! Happy coding! <click>

------------------------------------------------------------------------------
            This document and all coding in this package done by
                            Magister of Phazix.

 Additional programming info can be found at Magister's Programming Outpost,
                 located at http://magister.home.ml.org.
  
                      Copyright (c) 1998 Brent Kerby
