For two-color symmetry we want that the transformation is its own inverse. Then we could not say which part of the kaleidoscopic image has the original colors and which part has the transformed colors without knowing the input image.

The most simple transformation with these properties is making the negative image:

r → 255 – r, g → 255 – g and b → 255 – b.

This inverts the color cube and maps black to white, red to cyan, blue to yellow and green to magenta. The result is often quite bewildering. Consider this photo of a bee on lavender blossoms:

Its negative image is quite strange:

This is no surprise for those who do analogue photography. The developed negative does not give a good idea of the final image both in color or black and white photography. The problem is that dark parts become light and inversely. This makes it difficult to recognize details of the input image in the kaleidoscopic image with transformed colors.

We might want to have a different transformation. The grays should not be changed; especially, black remains black and white remains white. The other corners of the color cube should map as for making the negative image. To get this, we dissect the pixel color into two parts: an achromatic grey and a fully saturated color. The grey part results from the smallest of the red, green or blue components. If

s = min(r, g, b),

then (s,s,s) are the coordinates of the grey. It has no hue, zero saturation (colorfulness) and its intensity is given by s. By the way, you can call s “value” or “brightness” or whatever, see https://en.wikipedia.org/wiki/HSL_and_HSV. Subtracting this, we get the color part with components (r-s,g-s,b-s). At least one of these components is equal to zero, which means that we have a fully saturated color. Its hue depends on the input image. The largest of the three color components:

L = max(r, g, b)

defines its intensity. It is L-s.

To transform the color we leave the gray part as it is. For the saturated color part we want the smallest component to become the largest and inversely. This is easiest done by making a negative such as r-s→(L-s)-r-s=L-r. Adding the gray part, we get a simple transformation:

r → L+s-r,

g → L+s-g and

b → L+s-b.

You can easily verify that this transforms the corners of the color cube as desired and that this transformation is its own inverse. Using it on the image of the bee we get:

In spite of the false colors we recognize the bee very well. For some images you might prefer this transformation.

Then, we can use both transformations together, one after the other. The result does not depend on the order of execution. This combination exchanges black and white, whereas the other corners of the color cube stay in place. We get something strange like this:

]]>

First think about the simple square lattice. How would you tell a human or a computer to build it ? You could say: “Place squares side by side in rows and columns.” This defines the geometrical structure and does about the same as the functions X(x,y) and Y(x,y) of the kaleidoscope, which can generate rosettes, friezes or tilings of the plane. Now consider the checkerboard and how to get it. You could say: “Make a square lattice and for each square look at the sum of its row and column numbers. If the sum is an even number, then paint the square black, and else paint the square white.” Think of this sum as an extra function W(x,y) for defining the color symmetry.

Such a function W(x,y) is useful to generate kaleidoscopic images with two-color symmetry. For a pixel at the position (x,y) we look up the color of the input image at the mapped position [X(x,y),Y(x,y)]. For positive W(x,y)>>0 the pixel gets this color without change, and for negative W(x,y)<<0 we somehow transform the color. If W(x,y) is near 0 then we use an interpolated color. Clearly, symmetries of W must relate to symmetries of X and Y as in the example of the checkerboard.

For further discussion, we use the more convenient complex numbers z=x+i*y for points in the plane and a complex function Z(z)=X(x,y)+i*Y(x,y) for the mapping. A complex function S(z) is a symmetry of the function Z if Z(S(z))=Z(z) for all z. Thus the kaleidoscope will look up the same point in the input image for z and for S(z). Then, both points will have the same color in the kaleidoscopic image if use no color transformation. To get two-color symmetry we make that W(S(z))=-W(z) and use it to control a suitable transformation of color. This makes that z and S(z) in general have different colors. Then the symmetry S(z) is broken in the image but S(S(z)) is still a symmetry because W(S(S(Z)))=-W(S(z))=W(z).

For the checkerboard, the important symmetries S(z) are translations in x or y-direction: S(x,y)=(x+a,y) and S(x,y)=(x,y+a), where a is the length of the sides of the squares. As color control function we can use W(x,y)=cos(π*x/a)*cos(π*y/a), which is positive on some squares and negative on their neighboring squares. We get a checkerboard pattern if we draw white pixels for W>0 and black pixels for W<0. Note that doubled translations by 2*a in x or y-direction are symmetries of the checkerboard.

]]>

f(r,φ)=f(1/r,-φ)=f(1/r,φ+π/p)=f(r,-φ-π/p) for rosettes with p-fold rotational symmetry.

From f(r,φ)=f(r,-φ-π/p) we see that a mirror symmetry arises at φ=-π/(2p). This gives us

and

I found it quite difficult to get a nice image that clearly shows these symmetries. The photo of a butterfly (a red admiral) gave

This image has only three-fold rotational symmetry. Look at the pale violet flowers to discover the glide reflection. The center of the rotational symmetry is at the center of the orange shapes.

]]>

and

where I have used that cos{lp(φ+π/p)}=cos(lpφ+lπ). Using a photo of a yellow dragonfly I get this result of six-fold rotational symmetry:

Note the yellow horseshoe like shapes. The glide reflection makes that there are 12 copies, six opened towards the center and six opened outwards. Together they form a prominent star.

]]>

In “parallel” means that the rosette has both symmetries at the same time and thus the mapping functions have to obey to the conditions f(r,φ)=f(1/r,φ)=f(r,-φ)=f(1/r,-φ). They are then

and

These mirror symmetries for themselves make a rather “static” image of four-fold symmetry. To make this effect stronger I also used a four-fold rotational symmetry with p=4 to get this:

Here I used my photo of a fly on a Muscari flower. For the X-function I put a simple cos(4φ) term and for the Y-function a cos(8φ) term.

In “series” means that we use both mirrors one after the other. This gives a rotation by 180 degrees with the center at the intersections of the two mirror axis. The image should have this symmetry which we can write as f(r,φ)=f(1/r,φ). Thus the mapping functions are

and

Because of the dynamical nature of this added rotation symmetry I preferred a rosette with 5-fold rotational symmetry. My photo of a goldsmith beetle gave this

Although this is a rather abstract ornament you can see details of the green back of the beetle and the rose flower.

I am happy that I can do this work on symmetries and create images but I cannot forget that there are many children, women and men in danger. We could help them by supporting Doctors Without Borders for example. During the next days you can get a lot of games for a small donation from https://www.humblebundle.com/freedom.

]]>

As rosettes are essentially Friezes wound around a central point we can use all the symmetries of Friezes for rosettes too, although they may be distorted. As an example, we now look at the distorted mirror symmetry at r=1. It imposes the condition f(1/r)=f(r) on the mapping functions, thus they are

and

The images are somehow bloated around r=1. This makes it difficult to create nice images. A photo of a minstrel bug gave this

and a moth morphed into that

See how the outside of the rosette is wrapped and squashed to the inside. Near the symmetry “axis” r=1 we get strong deformations and abstract designs. Far away details of the input image remain.

And now for something different: Let’s fire the anti-science fools on april 1 !

]]>

Here if have used a butterfly as input image. You can recognise some details of its wings and legs as well as parts of the yellow flower it sits on. The mirror symmetry makes that the image becomes more abstract. The mapping function is

function mapping(x,y){ imageZero(x,y); imageAdd(0.7,0,0,0,1,6); imageAdd(0,0,1,0,2,3); imageAdd(0.5,0,0,0,2,12); }

Make your own creations. This is easy: Load http://bookofgames.ch/rosette.html in your browser. Then download the html and JavaScript codes using those buttons. Open the downloaded html page in your browser and edit the JavaScript file in a text editor. Save your changes and reload the html page. Send a comment if you get stuck and need help or if you want to share your results.

]]>

Here z=x+iy relates to the position (x,y) of a pixel of the output image. We then have to sample the color of the input image at the position (X,Y) given by Z=X+iY. This gives the color of the pixel. With polar coordinates, where r²=x²+y² and tan(φ)=y/x, we see easily that we have p-fold rotational symmetry. A rotation of (x,y) by 2π/p changes φ to φ+2π/p and gives the same Z. Thus we get the same color for the pixel at the rotated position.

With p=5 I got this result:

The input image is “Bee on lavender blossom” of my photography blog “looking closer“. I used my program “rosette“, which is an interactive web page. You can download its html and JavaScript files to make your own experiments. To get images of different symmetry you simply have to change the code of the last function: mapping(x,y), see later. Here I choose the “nearest” input image pixel interpolation option, which in fact does not smooth pixels. Thus you can see that some pixels are strongly magnified and distorted at the center of the image because of the large power of *r* in the mapping function. At the border you see a frieze of five somehow distorted bees following each other.

We can get more interesting images if we use several combinations of z together with its complex conjugate. As proposed by Farris:

where (m-n) has is a multiple of p for p-fold rotational symmetry. For the power of r we can use any integer number resulting in a more detailed center of the rosette. I find it convenient to rearrange these terms and to use real coordinates. Thus

and

which is convenient for computation and discussing symmetries. Programming becomes easy, see the end of the rosette.js file. The function mapping(x,y) has first a call to the function imageZero(x,y) for initialization. Then we make calls to the function imageAdd(a,b,c,d,rPower,phiPower), adding terms of the above equations with rPower=k and phiPower=l*p.

We can get more varied images with more detail, such as this one:

Again, I did not use smoothing of pixels.

One last thing: Fight for reason and support the March for Science.

]]>

Each pixel of the output image has integer indices (h,k) for its row and column. After an offset and a scaling we get a point (x,y) in 2-dimensional space:

x=outputScale*(h-outputOffsetX) and

y=outputScale*(k-outputOffsetY).

We can choose the offsets and the scale interactively. Changing the offset we move a center of symmetry around in the image. We can zoom in or out by varying the scale.

Then a mapping transforms the point (x,y) to an image point (X,Y). This mapping defines the symmetry. For theoretical work we can use complex coordinates z=x+i*y and Z=X+i*Y. But in the end we have to use real numbers x and y for our programming.

The image point (X,Y) points to the input image and shows where to sample its color, which will be the color of the output image pixel at (h,k). For versatility we rotate, scale and shift the coordinates to get row and column values:

H=inputScale*(X*cos(α)-Y*sin(α))+inputOffsetX and

K=inputScale*(X*sin(α)+Y*cos(α))+inputOffsetY.

To find the best image we choose the angle α, the input scale and the input offsets interactively. Obviously, H and K are not integers and we need some pixel interpolation. For fast exploration we simply take the color of the nearest pixel. To get good image quality we can change to linear or cubic interpolation.

And now for something completely different: Fight for your rights and support the ACLU.

]]>

If x is in a limited range, 0<x_min<x<x_max, then you can approximate log(x) with a simple table. Because of the singularity at x=0 and other properties of the logarithm you should use a size of about 1000*(x_max/x_min) to get a good approximation. For more details see http://bookofgames.ch/specialFastLog.html.

Results are similar as for the exponential function: Firefox is about 4 times faster than Chrome and the approximation of log(x) is about 4 times faster too.

I have put the timing loop in a separate function. This lowered the measured times considerably. I suppose that the JavaScript engine made better optimizations.

]]>