Shape Rules

John Horigan edited this page Mar 16, 2015 · 2 revisions

Shape rules describe how to draw a given shape. A shape is composed of some number of primitive shapes (squares, circles, triangles, and fills) and non-primitive shapes. There must be at least one rule for each shape referenced or Context Free/CFDG will give you an error. A shape rule gives the name of a shape, lists is parameters if there are any, and introduces the rules for drawing the shape. Each rule contains shape replacements, variable declarations, and control flow elements.

shape LeftOrRightFlower 
rule {
    LeftFlower []
}
rule {
    LeftFlower [flip 90]
}

In this example the shape LeftOrRightFlower is declared with no parameters and two rules are then declared for how to draw a LeftOrRightFlower.

If a shape has just one rule then it is OK to omit the rule keyword and just list the shape replacements after the shape declaration:

shape FLOWER {
    CIRCLE [size 2]
    loop 6 [r 60] LeftOrRightFlower [size 0.4 y 3]
}

The loop line is an example of a control flow element.

After Context Free/CFDG picks a rule it executes it by drawing each of the shape replacements contained within the rule. Primitive shapes are drawn right away and non-primitive shapes are put in the evaluation queue and drawn later.

Overlapping Shapes and the Order of Drawing

There is no guaranteed order to the drawing of shapes. The currently implemented rendering algorithm renders larger shapes first, so smaller shapes will be drawn later, on top of them. But this must not be relied upon, as the algorithm has changed in the past and may change again. There are only two guaranteed orders to drawing: primitive shapes in a rule draw in order before anything else, and shapes with a larger z-axis coordinate draw on top of shapes with smaller z, regardless of when they were rendered.

rule foo1 {
    SQUARE {}
    CIRCLE { b 1 }           // draws on top of the blank square
    foo1 { r 45 x 1 s 0.9 }  // draws on top of both
}
 
rule foo2 {
    CIRCLE { b 1 z 1 }
    SQUARE {}                // drawn under the white circle
    foo2 { r 45 x 1 s 0.9 }
}
 
rule foo3 {
    SQUARE {}
    CIRCLE { b 1 }
    foo3 { z -1 r 45 x 1 s 0.9 }  // drawn under both
}

Randomness, Rule Weights, and Having Multiple Rules for a Shape

If there is only one rule for all the shapes in a CFDG file then the result will always look the same. But when there is more than one rule for a given shape then Context Free/CFDG has to choose which one to use at random each time the shape is invoked. The probability that a particular rule will be used is proportional to its weight. The exact equation is the rule's weight divided by the sum of the rule weights for the given shape.

shape aShape 
rule 0.5 {  // probability is 0.5 / (0.5 + 1 + 2), or 1/7
    SQUARE []
}
rule {      // probability is 1 / (0.5 + 1 + 2), or 2/7
    CIRCLE []
}
rule 2 {    // probability is 2 / (0.5 + 1 + 2), or 4/7
    TRIANGLE []
}

Another use for multiple rules is to provide an ending condition to an infinite recursion:

startshape triArc
CF::Background = [b -1]  // looks more cool on black
 
shape triArc
rule {
    TRIANGLE [r -2 sat 1 b 1]
    triArc [x 1 r 4 hue 4]  // Never shrinks! 
}                           // This recursion will never end...
rule 0.01 {
    TRIANGLE [r -45 sat 1 b 1]
    triArc [y -1 r -90 hue 4]
}
rule 0.001 {} // ... unless we execute this rule

Percentages in Rule Weights

In expressions, a percentage is simply a short-hand for a number divided by 100; for example, 9.6% is directly converted to 0.096. But percentages in rule weights are treated specially. If a shape's rules have a mixture of percentages and ordinary constants and the percentages add up to <100% then the probability for rules with percentages is exactly equal to the percentage value. Any left-over probability is divided among the remaining rules according to its weight. Context Free will issue a warning if the percentage weights sum to more than 100%, or they sum to 100% and there are non-percentage weights, or they sum to <100% and there are no non-percentage weights.

shape aShape 
rule 50% {  // probability is 0.5
    SQUARE []
}
rule {      // probability is 1 * (1 - 0.5) / (1 + 2), or 1/6
    CIRCLE []
}
rule 2 {    // probability is 2 * (1 - 0.5) / (1 + 2), or 1/3
    TRIANGLE []
}

Shape Parameters

A shape can have parameters that are defined when the shape is invoked (either in a shape replacement or in a variable declaration). The parameter values provided at invocation are stored with the shape's state and are bound to parameter variables when the shape is executed. For example, the 242 shapes declared in the i_curves.cfdg example can be reduced to one parameterized rule:

shape curve(number shrink, number turn)
{
  SQUARE []
  curve(=) [[y 0.5 r turn s shrink y 0.5]]
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.