Typical fractal images are raster images. The image file (typically, jpg, bmp, gif, etc.) must store the color information for each pixel, and that's about it (except for possible compression information). The image only exists as an array of colored dots. While the image can easily be resized in a graphics program, this involves making up information to guess what would have been there for the new pixels.
Vector images are created using discrete shapes like lines, curves, solid fills, etc., and are not tied to a particular resolution (in concept, anyway). This makes them very scaleable--design the image small and then render it as large as necessary for the print job at hand. Any conversion to pixels is done at the final print resolution using the exact definitions of the shapes, so there are no "jaggies" or blurry areas from resizing a raster image.
r = cabs(z - circlecenter)The "error" quantity is represents the location of the point relative to the circle. If it is negative, the point is inside, and positive means outside. Coloring by the absolute value of error (using the abs() function) will give a of color that is centered on the desired circle. This figure shows the black circle and the reddish inside and bluish outside regions.
error = r - radius
Discrete points can be drawn by considering them as circles with a radius of 0:
r = cabs(z - zpoint)
x = real(z)The sign of f reflects which side of the curve the point is on. To show the curve, color by the absolute value of f with "Repeat Gradient" turned off. Crank up the "Color Density" to make the line thinner.
y = imag(z)
f = (some function of x & y that is 0 for points on the curve)
Polar curves, like rose curves, are expressed in terms of r and t, the magnitude and angle of the complex number, instead of x and y. For closed curves (the curve comes back and closes on itself), life is a bit easier. For example, here we have the rose curve, r = cos(5t). Since the cosine function is periodic, the true value of t is irrelevant. This is good, since the atan2() function, used to get the angle of a complex number, only returns values between -p and p. In fact, any routine for getting an angle from a complex number will only return a value in a range of width 2p. But once the angle is found, the desired magnitude is determined, and compared with the actual magnitude. In general:
t = atan2(z)Open curves can be more difficult, since an exact value for t can't be determined. For example, consider the linear spiral r = t. The point corresponding to r = 3p and t = 3p is on the spiral. However, the atan2() function will never return a value of 3p, so this point can't be found that way. If the relationship between r and t can be inverted, then the search can be performed on r:
rdesired = (some function depending on the desired curve)
ractual = cabs(z)
error = ractual - rdesired
r = cabs(z)The problem here (aside from solving the polar equation for r) is that this method won't work for negative r values. Strictly speaking, r is a polar coordinate, not a distance. As such, it can be negative. I'll leave it to you to figure out what to do then. :-)
t = (inverted function, t in terms of r)
x = r*cos(t)
y = r*sin(t)
zdesired = x + flip(y)
error = z - zdesired
t = (z - z0) /(z1 - z0).In this formulation, t = 0 at one endpoint z0 and t = 1 at the other endpoint z1. Along the line between the endpoints, t varies smoothly between 0 and 1. Furthermore, even though z, z0, and z1 are all complex numbers, t will be a pure real number (imaginary part = 0) for any point on the line. This gives us an easy way to see if a point is on the line: form t as above for each pixel; check to see if the real part of t is between 0 and 1; use the imaginary part of t to determine how far away from the line the pixel is.
Here we see the pieces and how they come together. The top half of the image is the final single line, at twice the scale of the images in the bottom. In the bottom half, the image in the bottom right is a combination of three layers, each shown in the other three quadrants. The bottom right picture has two diagonal lines that run from top left to bottom right, one diagonal from bottom left to top right, and a series of concentric ovals. The single line drawn in the top is along the single line from bottom left to top right, between the other pair of lines. The real part of the parameter is shown in red in the top left quadrant. The black end corresponds to 0 and the white end to 1, so the desired line fits exactly in this band.
The top right of the bottom half shows the imaginary part of t, and is colored in diagonal bands from black to green to white. Our line lies on the boundary between the black and white areas. Moving away from the line and above it, the colors start at black and move to green, then to white. This indicates that the imaginary part of t is positive on this side and increases away from the line. On the other side, the imaginary part is negative. So the imaginary part of t can be used to tell how far away from the line the pixel is, and on which side.
The two parts are combined in the top layer, shown in blue in the bottom left quadrant. Just using the imaginary part of t when the real part is between 0 and 1 would lead to sharp breaks in color at the endpoints. While this may be desired in some applications, a better solution is to put round caps on the ends. These caps are made by looking at both the real and imaginary parts when the real part of t is outside of the range [0, 1]. This coloring makes a "glow" around the line, which can be adjusted through the Color Density setting. In the layer shown, white corresponds to being very close to the line, then blue further away, then black, even further away. This layer has multiple bands; limiting the coloring to one band is accomplished by clearing the "Repeat Gradient" box.
x = Ax t2 + Bx t + Cxwhere the coefficients are determined by the control points. They are set such that t = 0 at the first control point and t = 1 at the third point. Cubic splines are similar, except that they use four control points, and the x and y equations are cubic polynomials in t. The problem with these polynomial equations is that they can't be inverted; given x and y, t cannot be uniquely determined.
y = Ay t2 + By t + Cy
In situations where the equation of the curve can't be inverted, one can always do it the hard way. That is, choose many values of the parameter and determine the x and y values. Then, check the pixel against this (x, y) point. Move on to the next parameter choice, and color the pixel according to the closest pixel. If you use this method, use a relatively small number of parameter values while designing your image, then increase it for final rendering to close the gaps between points.
For polygonal shapes, I have used two different methods. The first is related to the above technique for polar curves. Imagine standing at point P, somewhere inside a square. The four corners of the square are labeled A, B, C, and D, in order. Then, you can make the angle from corner A to point P to corner B. Then angle BPC from corner B to point P to corner C. Then, angles CPD and DPA. Since you're standing inside the square, the sum of these four angles will be one complete turn around point P, or 360 degrees. This will be the case for any point inside the square, indeed, inside any convex shape (none of the sides are "caved in"). If P is outside, then the sum will be less than 360 degrees, and will decrease the further away from the shape P is.
The other method uses the line parameterization from above. For each line (which is a side of a polygon), the imaginary part of the parameter t measures how far away the point is from the line, and on what side (positive on one side, negative on the other). Now imagine walking around a dining room table. You can walk around it in either direction; in one direction, you can keep your left hand on the table all the way around. In the other direction, your right hand can stay on the table. So, if you set up the corner points of your polygon in a coherent fashion (all clockwise or all counter-clockwise), then you can simply check if the imaginary part of the parameter is the same sign for all the sides of the polygon. If you establish the four corner points of a convex quadrilateral in a counter-clockwise fashion, and all four parameters have a positive imaginary part for a given pixel, then that pixel is inside the quadrilateral.
Squares and rectangles with sides parallel to the real and imaginary axes are particularly easy. If you had a rectangle with corners at (1, 2), (1, 3), (4, 3), and (4, 2), then you can check to see if the real part of a pixel's coordinates are between 1 and 4, and the imaginary part of the pixel's coordinates are between 2 and 3. If both of these conditions hold, then the pixel is inside the rectangle. If either condition is violated, then the pixel is outside.
One way to speed up complex shape calculations is to use a bounding shape. For example, if you're doing a 37-segment curve that completely lives inside a circle, then see if the pixel is inside or outside of the circle. If it is outside, then you can go on to the next point without having to run through the entire calculation.
In your quest for speed, don't be tempted to use the "Guessing" drawing mode. Your image will probably have scads of thin lines or small points in it, and the "Guessing" mode can pass over miss parts, leaving gaps and blank spots. Be sure to use one of the linear modes, either "One-pass" or "Multi-pass."
|This is an Apollonian packing, in which the triangular gap between three touching circles is filled with another circle. That leaves three more gaps, which are filled by three smaller circles, etc. It was rendered using transforms; each circle was created from its own transform. I wrote a separate program to determine the locations of the circles in the image and to create the upr.|
I call this image, "Harmony." It is composed completely of overlapping black and white solid circles. Each circle is slightly smaller than the previous, the opposite color, and tangent to it at one point. To avoid having to place a zillion transforms, this is all accomplished in the calculation formula.
|An exercise in rendering discrete points. This one shows the location of the Gaussian primes in the complex plane. Each dot is actually a small diamond. The blue diamond in the middle represents the point (0, 0).|
A basket created with 19 layers of rose curves. The layers used the "Rose Range Lite" coloring with the "Pixel" formula. Each layer merged using the "Darken" mode at 100% opacity so that each curve would show through equally.
|This demonstration of drawing lines uses 8 layers of "Astroid String Art" coloring. This formula offers the option of coloring by the magnitude or angle of z when it approached the line. The "Lighten" merge mode gives the effect of depth; some lines appear to be in front of or behind others.|
This Hilbert curve variant is made of four layers. In each layer, the curve is drawn as a series of lines, where the lines were computed in the formula, rather than laid down with transforms. The color comes from having a different stage of the curve and different gradient for each layer, and merging them all with the "Difference" mode.
|Here is a variation on the "metagon" shape. The image has three layers. The bottom layer colors by the pixel's membership in a polygonal area. That gives rise to the overall orange and cyan sweeps. The second layer, merged with the "Difference" mode, colors by the angle between the pixel and a side of the polygon. That creates the angular gradation in colors within the sweeps. The top layer is simply a mask to cut off the main design from some residual smudge, now tastefully colored black.|
Pentominoes are puzzle pieces made up of five squares. Even though they are concave shapes, they are easily drawn by drawing each of the five squares separately. These were drawn using the "Pentomino Color" transform. Pentominoes also have the interesting feature that a large version of any of the twelve shapes can be made from nine of the remaining pieces. This was the basis for my "Pentominoes" image.
|Penrose tiles are shapes that can also lead to fractal tilings. Here, I illustrate the "deflation" property of the "kite and dart" tiles. The overall image is broken into five kite shapes. Each kite is then broken into two smaller kites and a dart, and so on for five iterations. Each iteration is a separate layer; each kite on a layer is its own transform.|
To finish, here's a sample using a cubic (Bezier) spline. Each letter was made of two spline curves (and two layers). Each curve, in turn, was made of hundreds of individual points. Each point had to be checked against every pixel in the image. As a cheat, I made the lines thick and one color so I could use the "Guessing" drawing mode on each layer.