Skip to content

Archive

Tag: Fractals

Fern fractals are another class of iterative fractals, a type of iterative function system, that draws rather good images of ferns. And with a bit of tweaking of the input variables, the ferns of various sizes and shapes can be created using the same code.

A Fern Fractal

A Fern Fractal

To create a fractal fern you first need to supply a rather large amount of input variables that will control the shape of the fern. So in the application, the variables are stored in a few arrays.

            double[] dA = new double[2];
            double[] dB = new double[6];
            double[] dC = new double[6];
            double[] dD = new double[6];
            int[] iRand = new int[4];

            dA[0] = 0;
            dA[1] = 0.16;
            dB[0] = 0.2;
            dB[1] = -0.26;
            dB[2] = 0;
            dB[3] = 0.23;
            dB[4] = 0.22;
            dB[5] = 1.6;
            dC[0] = -0.15;
            dC[1] = 0.28;
            dC[2] = 0;
            dC[3] = 0.26;
            dC[4] = 0.25;
            dC[5] = 0.44;
            dD[0] = 0.85;
            dD[1] = 0.04;
            dD[2] = 0;
            dD[3] = -0.04;
            dD[4] = 0.85;
            dD[5] = 1.6;
            iRand[0] = 1;
            iRand[1] = 8;
            iRand[2] = 15;
            iRand[3] = 85;

So, now we have arrays dA, dB, dC, dD and iRand. Very simply, the values in dA control the rachis of the fern (the main stem for those, like me, who weren’t listening in biology class), dB controls the left pinna (a “leaflet”), dC controls the right pinna, and dD controls the main shape of a frond (the fern’s “leaf”). iRand contains the probability that a value from each of the four arrays will be used.

Now the main function iterates through a specified number of points (which in the sample on this page, I used 100000 iterations), by first setting the current x and y coordinate to 0, and then it starts the iteration.

A random number is generated between 0 and 1, and if the number is less than iRand[0] then it calculates the new xy coordinate based on the dA array. It then checks each probability through all the probabililities, each time calculating the xy coordinate using the array and calculation specified at that value.

Once the new coordinates are calculated, the point is drawn using the desired colour, and then the function loops to the next iteration, using these coordinates as the base coordinates for the next iteration.

Once the function has iterated enough times, the function exits, and you should be left with a decent fern.

Half the fun of this type of fractal is playing around with the input variables to see how they affect the output, so have fun. The full source code is available here.

        public void GenerateFern(Graphics g, int iIterations, double dStartX, double dStartY, double dScale, double[] dA, double[] dB, double[] dC, double[] dD, int[] iRand, Color oColor)
        {
            FastRandom rnd = new FastRandom();
            int iRandNum;
            double dX = 0;
            double dY = 0;
            double dNewX = 0;
            double dNewY = 0;

            for (int i = 0; i < iIterations; i++)
            {

                iRandNum = rnd.Next(0, 100);
                if (iRandNum < iRand[0])
                {
                    dNewX = dA[0];
                    dNewY = dA[1] * dY;

                }
                else if (iRandNum < iRand[1])
                {
                    dNewX = (dB[0] * dX) + (dB[1] * dY) + dB[2];
                    dNewY = (dB[3] * dX) + (dB[4] * dY) + dB[5];
                }
                else if (iRandNum < iRand[2])
                {
                    dNewX = (dC[0] * dX) + (dC[1] * dY) + dC[2];
                    dNewY = (dC[3] * dX) + (dC[4] * dY) + dC[5];
                }
                else
                {
                    dNewX = (dD[0] * dX) + (dD[1] * dY) + dD[2];
                    dNewY = (dD[3] * dX) + (dD[4] * dY) + dD[5];
                }
                dX = dNewX;
                dY = dNewY;
                g.FillRectangle(new SolidBrush(oColor), (float)(dStartX + (dNewX * dScale)), (float)(dStartY - (dNewY * dScale)), 1, 1);
            }

        }
Share

Sierpinski Triangles

Sierpinski Triangles


Sierpinski triangles and squares are relatively easy fractals to draw, and although the principle is the same I will handle the two separately.

For Sierpinski triangles, you take your initial triangle (an equilateral triangle works nicely) and then finding the midpoints of each side and connecting them, break up the triangle into four new triangles. Then for each of the three triangles on the outside, repeat this process until you reach the desired recursion depth. The middle triangle is left alone, and remains unfilled. When you reach the recursion depth you require, you can then colour the triangle the desired colour.

Sierpinski Squares

Sierpinski Squares

Sierpinksi squares work in a similar way, except, instead of using the midpoint, you divide each edge into three pieces, and then split up the initial square into nine new squares. Repeat this process for the eight outer squares, leaving the middle square, as in for the triangles, alone. Once you reach the desired recursion depth, you then colour the square the desired colour.

So, then, the code to generate this is as follows. There is nothing tricky at all about it. In both cases, if the recursion depth is not yet met, the points are calculated, and the function is called recursively for each new shape, and if you have recursed far enough, then draw the shape based on the desired colour and coordinates. The coordinates of the initial shapes are passed as doubles to the functions, such as x1, y1 etc.

        public void GenerateSquare(Graphics g, int iIterations, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, Color oColor)
        {
            if (iIterations > 1)
            {
                double midx1a = x1 + ((x2 - x1) / 3);
                double midy1a = y1;
                double midx1b = x1 + ((x2 - x1) / 3 * 2);
                double midy1b = y1;
                double midx2a = x2;
                double midy2a = y2 + ((y3 - y2) / 3);
                double midx2b = x2;
                double midy2b = y2 + ((y3 - y2) / 3 * 2);
                double midx3a = x1 + ((x3 - x4) / 3);
                double midy3a = y3;
                double midx3b = x1 + ((x3 - x4) / 3 * 2);
                double midy3b = y3;
                double midx4a = x1;
                double midy4a = y1 + ((y4 - y1) / 3);
                double midx4b = x1;
                double midy4b = y1 + ((y4 - y1) / 3 * 2);
                double midx5a = x1 + ((x2 - x1) / 3);
                double midy5a = y1 + ((y4 - y1) / 3);
                double midx5b = x1 + ((x2 - x1) / 3 * 2);
                double midy5b = y1 + ((y4 - y1) / 3);
                double midx5d = x1 + ((x2 - x1) / 3);
                double midy5d = y1 + ((y4 - y1) / 3 * 2);
                double midx5c = x1 + ((x2 - x1) / 3 * 2);
                double midy5c = y1 + ((y4 - y1) / 3 * 2);

                GenerateSquare(g, iIterations - 1, x1, y1, midx1a, midy1a, midx5a, midy5a, midx4a, midy4a, oColor);
                GenerateSquare(g, iIterations - 1, midx1a, midy1a, midx1b, midy1b, midx5b, midy5b, midx5a, midy5a, oColor);
                GenerateSquare(g, iIterations - 1, midx1b, midy1b, x2, y2, midx2a, midy2a, midx5b, midy5b, oColor);
                GenerateSquare(g, iIterations - 1, midx5b, midy5b, midx2a, midy2a, midx2b, midy2b, midx5c, midy5c, oColor);
                GenerateSquare(g, iIterations - 1, midx5c, midy5c, midx2b, midy2b, x3, y3, midx3b, midy3b, oColor);
                GenerateSquare(g, iIterations - 1, midx5d, midy5d, midx5c, midy5c, midx3b, midy3b, midx3a, midy3a, oColor);
                GenerateSquare(g, iIterations - 1, midx4b, midy4b, midx5d, midy5d, midx3a, midy3a, x4, y4, oColor);
                GenerateSquare(g, iIterations - 1, midx4a, midy4a, midx5a, midy5a, midx5d, midy5d, midx4b, midy4b, oColor);
            }
            else
            {
                Pen o = new Pen(new SolidBrush(oColor));
                PointF[] points = new PointF[4];
                points[0] = new PointF((float)x1, (float)y1);
                points[1] = new PointF((float)x2, (float)y2);
                points[2] = new PointF((float)x3, (float)y3);
                points[3] = new PointF((float)x4, (float)y4);

                g.FillPolygon(new SolidBrush(oColor), points);
            }
        
        }

        public void GenerateTriangle(Graphics g, int iIterations, double x1, double y1, double x2, double y2, double x3, double y3, Color oColor)
        {
           
            if (iIterations > 1)
            {
                double midx1 = (x1 + x2) / 2;
                double midy1 = (y1 + y2) / 2;
                double midx2 = (x2 + x3) / 2;
                double midy2 = (y2 + y3) / 2;
                double midx3 = (x3 + x1) / 2;
                double midy3 = (y3 + y1) / 2;
                GenerateTriangle(g, iIterations - 1, x1, y1, midx1, midy1, midx3, midy3, oColor);
                GenerateTriangle(g, iIterations - 1, midx1, midy1, x2, y2, midx2, midy2, oColor);
                GenerateTriangle(g, iIterations - 1, midx3, midy3, midx2, midy2, x3, y3, oColor);
            }
            else
            {
                PointF[] points = new PointF[3];
                points[0] = new PointF((float)x1, (float)y1);
                points[1] = new PointF((float)x2, (float)y2);
                points[2] = new PointF((float)x3, (float)y3);

                g.FillPolygon(new SolidBrush(oColor), points);
            }
        }

You can get the full source code, which includes all the other fractals, here.

Share

This is the first in a series of posts that I am going to do on generating fractals using C#. All code was compiled using Visual Studio 2008.

The source code for the application, which includes all the fractals which will be covered in the series is available for download here.

Everybody always covers the Mandelbrot as the fractal of choice, so, in doing things a little differently, I decided to start with plasma fractals, and leave the Mandelbrot until later.

A plasma fractal

A plasma fractal


Assigning values to the four corners

Assigning values to the four corners

So, what is a plasma fractal exactly? A plasma fractal is generated by taking a rectangle with a width x and height y. Then to each corner in the rectangle, assign a random value between 0 and 1.

Find average for edges and centre

Find average for edges and centre


The next step is to find the midpoint of each side, and at that point, calculate the average value of the two points that this point is bisecting. A midpoint is also calculated, which is in the centre of the rectangle, which is the average of all four corners plus a random displacement.

Repeat process with the four new rectangles

Repeat process with the four new rectangles

Then for each of the four rectangle bounded by the new points, repeat the process, until all the pixels have been calculated.

After the values have been calculated, the values can be converted to colours using any method you like. Using the raw values unaltered will create a greyscale image very well with only adjusting the value to put it in the correct range.

Now, in the code to do this (which can be found in Plasma.cs in the source download), first, we have a few global variables

        public double gRoughness;
        public double gBigSize;
        FastRandom rnd;

The roughness is amount in which we are going to vary the value by for the midpoint.gBigSize is defined as the width plus the height of the image we are generating. FastRandom is a random number generator, which works exactly like the builtin random number generator, except that it is faster.

Our starting point is the Generate function that return a two-dimensional array of values where each entry in the array corresponds to an xy coordinate and the value is the calculated value, which will be converted into a colour.
continue reading…

Share