I'll describe a natural system (called a chief series for the algebraists in the eaves) to enumerate the symmetries of the cube (and coincidentally, at the same time symmetries of other related objects). The very end has code to apply the symmetries.
I require two concessions: a few values are unused (bits 2,3 cannot both be 1), and we actually apply the transformation rather than represent it as a big matrix. The first part is no big deal since you just loop i over {0,1}, then j over {0,1}, then k over {0,1,...,11} and use 32*i + 16*j + k. The second part should also be no big deal, as writing down the matrix is approximately the same as applying the transformation to three of the corners of the cube.
Now the description of the numbering:
The numbers 0 through 3 are used for the Klein four-group which consists of the 180 degree rotations. I suggest:
- 0 is the identity matrix
- 1 is the XY plane 180 degree rotation, (x,y,z) → (d−x,d−y,z)
- 2 is the YZ plane 180 degree rotation, (x,y,z) → (x,d−y,d−z)
- 3 is the XZ plane 180 degree rotation, (x,y,z) → (d−x,y,d−z)
Composition of these symmetries is simply XOR.
The numbers 4 through 11 are used for the 120 degree rotations of the tetrahedron sitting inside the cube. In other words, take a "main diagonal" of the cube, and rotate around it. It has a 3 fold symmetry, 120 degree rotations. Taking the diagonal from (0,0,0) to (d,d,d) one gets a particularly easy transformation: (x,y,z) → (y,z,x). At any rate, 4 through 7 are do that 120 degree rotation, and then do the 180 degree rotation 0 through 3. 8 through 11 are do that 120 rotation twice, so (x,y,z) → (z,x,y), and then the 180 degree rotation 0 through 3. Composition is a hassle (add the high nibble mod 3, but take one of the high nibbles and use it swirl the low nibble).
The numbers 16 through 27 are used for the rotational symmetries of the cube that switch the interior tetrahedrons. This is also very easy to describe as a transformation: if bit 8 is set, then apply the rotation you get by holding opposite vertical edges of the cube and rotating them 180: (x,y,z) → (y,x,d−z).
The numbers 32 through 43 and 48 through 59 are used for the reflectional symmetries of the cube (the first batch being symmetries of the two tetrahedra, and the second batch swapping two tetrahedra). I like to use the coordinate swap of the first two coordinates for this: (x,y,z) → (y,x,z).
Here is actual code in good ole javascript to do it:
applySymmetry = function( which, d, x, y, z ) { var t; // Peel off the "are we a reflection?" bit if( which & 32 ) { t=x; x=y; y=t; } // Peel off the "do we swap the tetrahedrons?" bit if( which & 16 ) { t=x; x=y; y=t; z=d-z; } // Now we are in tetrahedral group, peel off the "120-ness" switch( (which & (4+8) ) >> 2 ) { case 0: break; case 1: t=x; x=y; y=z; z=t; break; case 2: t=z; z=y; y=x; x=t; break; case 3: alert('Not cool dude'); break; } // Now we are in the Klein four group, peel off the "180-ness" switch( which & (1+2) ) { case 0: break; case 1: x=d-x; y=d-y; break; case 2: y=d-y; z=d-z; break; case 3: z=d-z; x=d-x; break; } return [x,y,z]; } applyAllSymmetries = function( d, x, y, z ) { // return the orbit var i, j, k, ret; ret = []; for( i = 0 ; i < 2 ; i++ ) { for( j = 0 ; j < 2 ; j++ ) { for( k = 0 ; k < 12 ; k++ ) { ret.push( applySymmetry( 32*i+16*j+k, d, x, y, z ) ); } } } return ret; }
This code is meant to exhibit the chief series. You could optimize the applySymmetry
function by combining the handling of bits 16 and 32, and you could optimize applyAllSymmetries
by using a "walk on the Cayley graph" (instead of using coset representatives like I am).
Also you could swap the order of bits 4 and 5 if you wanted to focus on the "tetrahedron versus cube" instead of "rotation versus reflection".