Context Free Art
How do you make two copies of a shape that are mirror images of each other, or three copies rotated 120° around a point? If the shapes are not random then it is straight forward. If the shapes are random then each copy will be different. Symmetry operations allow exact duplication of the design with various translation, rotation, and reflection symmetry operations applied.
The symmetry operations are applied to the whole design. Whatever is specified by your startshape will get the symmetry operations applied to it. But why not allow shape rules to specify symmetry operations for individual shape replacements? Because this would violate the context-free purity of Context Free/cfdg. There is no context-free grammar with separate parts that are random but identical. We get around this with symmetry operations by applying them to the entire design after the grammar is 'executed'. This is the same post-processing hack that is done with tiled rendering.
That said, there is still wiggle room if the design is explicitly impure (CF::Impure = 1). The clone statement allows designs to have localized symmetry.
In two dimensions there are two symmetry operations: cyclic and dihedral. Cyclic symmetry is simply rotational symmetry, the design is rotated around a point. Dihedral symmetry adds mirror symmetry, so the design and its reflection are rotated around a point.
If your design has frieze symmetry then you can specify Frieze group symmetry: p11g, p11m, p1m1, p2, p2mg, and p2mm. Um, just go click that Wikipedia link to find out what those mean.
Symmetry operations are enabled by adding a CF::Symmetry configuration declaration and assigning it a list of symmetry specifications:
CF::Symmetry = CF::Dihedral, 6 // snowflake symmetry
A symmetry specification starts with the symmetry token (e.g., CF::Dihedral) followed by one or more configuration numbers, all separated by commas. There can be many symmetry specifications in the list, each separated by a comma. A shape adjustment can also be a symmetry specification.
Cyclic symmetry is specified with CF::Cyclic, followed the order and an optional center of rotation (one or three numbers). The order must be ≥1.
CF::Symmetry = CF::Cyclic, 4 // four-fold rotation symmetry
CF::Symmetry = CF::Cyclic, 4, 2, 1 // four-fold rotation symmetry around the point (2,1)
The order of cyclic symmetry need not be an integer. If the order is not an integer then it will be rounded down to determine how many copies of the design to draw. But the angle between them will be determined by the full value, not the rounded value. So if the order is 3.5 then 3 copies will be drawn at an angle of 360°/3.5=102.86°.
Be very careful with using an expression for symmetry order. If you intend to have four-fold symmetry but your order expression is 3.999999 then you will get 3 copies 89.99999° apart. If your symmetry order is supposed to be an integer but it is not an integer constant then it is a good idea to round it to the nearest integer using the floor() function.
Dihedral symmetry is specified with CF::Dihedral, followed the order, an optional mirror angle and an optional center of rotation (one, two, three, or four numbers). The order must be ≥1 and also need not be an integer.
CF::Symmetry = CF::Dihedral, 4 // four-fold dihedral symmetry
CF::Symmetry = CF::Dihedral, 4, 2, 1 // four-fold dihedral symmetry around the point (2,1)
CF::Symmetry = CF::Dihedral, 4, 30 // four-fold dihedral symmetry, mirror angle at 30°
CF::Symmetry = CF::Dihedral, 4, 2, 1, 30 // four-fold dihedral symmetry around the point (2,1), mirror angle at 30°
p11g symmetry is specified with CF::p11g, followed by the vertical or horizontal glide reflection axis position (one number). If the frieze symmetry is horizontal then the glide reflection axis is horizontal. Otherwise it is vertical. The axis position parameter is optional and defaults to 0.
CF::Symmetry = CF::p11g, 0 // step symmetry with the mirror across the line y=0 |
|
p11m symmetry is specified with CF::p11m, followed by the vertical or horizontal mirror axis position (one number). If the frieze symmetry is horizontal then the mirror axis is horizontal. Otherwise it is vertical. The axis position parameter is optional and defaults to 0.
CF::Symmetry = CF::p11m, 0 // jump symmetry with the mirror across the line y=0 |
|
p1m1 symmetry is specified with CF::p1m1, followed by the vertical or horizontal mirror axis position (one number). If the frieze symmetry is horizontal then the mirror axis is vertical. Otherwise it is horizontal. The axis position parameter is optional and defaults to 0.
CF::Symmetry = CF::p1m1, 0 // sidle symmetry with the mirror across the line x=0 |
|
p2 symmetry is specified with CF::p2, followed by the center of 180° rotation (two numbers). The centre parameters are optional and default to (0,0).
CF::Symmetry = CF::p2, 0, 1 // spinning hop symmetry with the mirror around the point (0,1) |
|
p2mg symmetry is specified with CF::p2mg, followed by the center of 180° rotation (two numbers). If the frieze symmetry is horizontal then the glide reflection axis is horizontal. Otherwise it is vertical. The centre parameters are optional and default to (0,0).
CF::Symmetry = CF::p2mg, 0, 1 // spinning sidle symmetry with the mirror around the point (0,1) |
|
p2mm symmetry is specified with CF::p2mm, followed by the center of 180° rotation (two numbers). The centre parameters are optional and default to (0,0).
CF::Symmetry = CF::p2mm, 0, 1 // spinning jump symmetry with the mirror around the point (0,1) |
|
Designs with symmetry generated by one of the 17 Wallpaper groups can set CF::Symmetry and CF::Tile appropriately. All wallpaper group symmetries require that tiling be enabled. Most wallpaper groups have specific requirements for the symmetry of the tiling lattice, except for p1 and p2 group symmetry.
Group p1 is implemented by setting the CF::Tile variable (skew is permitted), as the only elements of the symmetry group are translations.
Group p2, specified by setting the CF::Tile variable (as with p1) and setting CF::Symmetry to CF::p2. This gives the tiling a 180 degree rotation. Although it has the same symmetry name as the Frieze group p2 above, it differs from Frieze version in that the setting of the CF::Tile has definite width and height, and can be skewed.
The centre parameters in group p2 indicates the centre of the 2-fold rotation (the blue diamond). The centre parameters are optional and default to (0, 0).
CF::Tile=[[s 2 3 skew 0 10]] // tile is 2 wide, 3 high, skewed tiling |
|
Due to either having a reflection (mirror) or glide reflection (reflection plus translation parallel to the reflection axis) or a combination of two of these, the tile of these groups needs to be rectangular.
The axis specification in groups pm, pg, and pmg indicate which axis (x or y) is the mirror axis (or glide axis for pg). The axis specification is zero for the x-axis and non-zero for the y-axis. The axis position parameter in groups pm and pg indicate the position of the mirror or glide axis. The axis position parameter is optional and defaults to 0. The centre parameters in groups pmm, pgg, and pmg indicate the point that the mirror and/or glide axes intersect. The centre parameters are optional and default to (0, 0).
This figure illustrates a diamond tile, which is required for cm and cmm group symmetry:
The green rhombus indicates the unit cell of the diamond tile. However, Context Free only supports tiling lattices with at least one axis coincident with the x or y axis. So either the red or blue parallelogram must be used for the tiling lattice. The most straight-forward method for specifying this skewed tiling lattice is to use the six parameter form of the transform adjustment. For a diamond tile that is 3 units wide and 2 units high you would use one of these two:
CF::Tile=[trans 0 0 3 0 4.5 1] // red parallelogram
CF::Tile=[trans 0 0 1.5 1 1.5 3] // blue parallelogram
in general:
CF::Tile=[trans 0 0 width 0 (width * 1.5) (height * 0.5)] // red parallelogram
CF::Tile=[trans 0 0 (width * 0.5) (height * 0.5) (width * 0.5) (height * 1.5)] // blue parallelogram
The mirror axis specification in group cm indicates which axis (x or y) is the mirror axis. The mirror axis specification is zero for the x-axis and non-zero for the y-axis. The axis position parameter in group cm indicates the position of mirror axis. The axis position parameter is optional and defaults to 0. The centre parameters in group cmm indicate the point that the mirror axes intersect. The centre parameters are optional and default to (0, 0).
As these groups have a 90° centre of rotation, they require the tiling grid to be square.
The centre parameters in groups p4, p4m, and p4g indicate the centre of the 4-fold rotation (the green squares). The centre parameters are optional and default to (0, 0).
These symmetry groups require a tile that fits on a hexagonal grid. There are two permissible hexagonal grids in Context Free: horizontal and vertical.
CF::Tile=[skew 15 15 s scale r -15] // horizontal hexagonal grid
CF::Tile=[skew 15 15 s scale r 15] // vertical hexagonal grid
The centre parameters in groups p3, p3m1, and p31m indicate the centre of the 3-fold rotation (the red triangles). The centre parameters in groups p6 and p6m indicate the centre of the 6-fold rotation. The centre parameters are optional and default to (0, 0).
All of the symmetry specifications above are convenience functions for specifying the affine transforms for the one and two-dimensional symmetry groups. CF::Cyclic generates n transforms; CF::Dihedral generates 2n transforms, p11g, p11m, p1m1, and p2 generate 2 transforms; and p2mg and p2mm generate 4 transforms.
CF::Symmetry = [], [r 120], [r 240] // equivalent to CF::Cyclic, 3
But affine transforms can be directly inserted into the symmetry transform list even if they are technically from symmetry groups:
CF::Symmetry = [skew 30 0], [[skew 30 0 r 120]], [[skew 30 0 r 240]] // skewed C3 symmetry