John Horigan edited this page Sep 15, 2016 · 3 revisions

Parameters are like variables, but they are evaluated at the invocation of a shape or path and bound to the parameter variables when the shape or path is executed.

startshape LeftOrRightFlower
shape curve(number shrink, number turn) {
  curve(shrink, turn) [[y 0.5 r turn s shrink y 0.5]]
stem = curve(0.97, 1)    // bind 0.97 to shrink and 1 to turn in shape curve, and bind shape curve to stem
shape LeftOrRightFlower 
rule {  LeftFlower [] }
rule {  LeftFlower [flip 90] }
shape LeftFlower { 
   curve(0.98, -3) [x  1.5 y 0.125 size 0.25]     // bind 0.98 to shrink and -3 to turn in shape curve, and invoke.
   curve(0.98, -2) [x  1.0 y 0.125 size 0.25]     // bind 0.98 to shrink and -2 to turn in shape curve, and invoke.
   curve(0.98, -1) [x  0.5 y 0.125 size 0.25]     // bind 0.98 to shrink and -1 to turn in shape curve, and invoke.
   curve(0.98,  1) [x -0.5 y 0.125 size 0.25]     // bind 0.98 to shrink and 1 to turn in shape curve, and invoke.
   curve(0.98,  2) [x -1.0 y 0.125 size 0.25]     // bind 0.98 to shrink and 2 to turn in shape curve, and invoke.
   curve(0.98,  3) [x -1.5 y 0.125 size 0.25]     // bind 0.98 to shrink and 3 to turn in shape curve, and invoke.
   stem [y 0.5]                                   // Invoke stem shape replacement variable
   Blossom [x -13.25 y 26.3]
shape Blossom {
   CIRCLE [size 2]
   loop 6 [r 60] LeftOrRightFlower [size 0.4 y 3]

Parameter Types

As you can see from the example, a parameter declaration is a type followed by a name. There are five parameter types: number, vector, adjustment, shape, and natural. The first four are equivalent to the numeric, vector, shape adjustment, and shape replacement variable types. The natural type is a numeric type that is constrained to be a non-negative integer. The type specifier is optional for number parameters. If you don't specify a parameter type then the parameter is assumed to be a number.

Number parameters

Number parameters are floating point numbers. They can be used in any expression within a rule or path.

Vector parameters

Vectors are tuples of floating point numbers. Some functions and shape adjustments can take vectors as arguments. You must specify the size of the vector like so: vector2 is a 2-tuple, vector3 is a 3-tuple, etc. Currently the largest vector size allowed is vector99.

Natural Parameters

Natural parameters are numeric parameters that are constrained to be natural numbers (non-negative integers). They can be used in any expression within a rule or path.

Adjustment parameters

There are two ways to use adjustment parameters: they can be inserted into a shape adjustment list using the new transform adjustment (which has the abbreviated form tr). Or the parameter can be directly inserted an expression that has adjustment type.

Shape parameters

Shape parameters are used in place of the shape name in a shape replacement.

startshape test
shape foo(scale)    // equivalent to shape foo(number scale)
  SQUARE [r 0..90 s scale]
shape recurse(adjustment change, shape thingie)
  thingie[]                      // invoke thingie with parameters already bound
  recurse(=) [transform change]  // can be abbreviated as trans
shape test {
  recurse( [[y 0.5 s 0.97 r 3 y 0.5]], foo(0.5) ) [] // bind 0.5 to scale in shape foo here

The above example shows how adjustment and shape parameters are passed to a shape and used within a shape.

Restriction to Preserve Context Free Purity

Allowing unrestricted use of parameters would change CFDG files from context free grammars to indexed grammars, which are considered context-sensitive. So there are two types of numeric parameters, each with its own restrictions.

Number Parameter Restrictions

Number parameters can either be one of two forms: purely local or purely non-local. A purely local expression does not depend on any incoming parameters: it does not reference an incoming parameter, does not reference a variable that depends on an incoming parameter, and does not reference a loop index which has loop bounds that depend on an incoming parameter. A purely non-local expression is simply a direct reference to an incoming parameter.

shape taper(number shrink) {
    SQUARE []
    taper(0.5) [size shrink]              // Allowed, 0.5 is constant
    taper(rand(0.5, 1.5)) [size shrink]   // Allowed, rand() is random, and 0.5 & 1.5 are constant
    taper(shrink) [size shrink]           // Allowed, shrink is a number parameter
    taper(=) [size shrink]                // Allowed, same as previous line
    taper(shrink * 0.5) [size shrink]     // Not allowed, expression includes a number parameter
    taper(rand(shrink * 2, shrink * 2.000000001)) [size shrink]
                                          // Not allowed, rand() is random but its arguments are not constant

Natural Parameter Restrictions

Natural parameters are restricted to be natural numbers (positive integers or zero). Any expression can be evaluated and bound to a natural parameter as long as it meets the natural number restrictions. The natural number restrictions are:

  • All constants must be natural numbers
  • All variables (local, global, and parameter variables) must be natural
  • The only permitted operators are: addition (+), proper subtraction (--), multiplication (*), exponent(^), comparison (<, >, <=, ≤, >=, ≥, ==, <>, ≠), and boolean (&&, ||, ^^, !).
    • Operands must be natural
    • Division (/), negation(-), and regular subtraction (-) are not allowed
  • Only permitted functions are: abs(), bitand(), bitor(), bitnot(), bitxor(), bitleft(), bitright(), div(), divides(), factorial(), if(), max(), min(), mod(), randint(), select(), and sg()
    • Arguments must be natural
shape fibspiral(natural last, natural current)
rule 0.001 {}
rule {
  quarter [sat 1 b 1]
  fibspiral(current, last+current)
    [h 30 x -1 y 1 r 90
     s ((last+current)/current)]

Range of Natural Numbers

A natural number must be ≥0 and ≤CF::MaxNatural. The default value for CF::MaxNatural is 1000, but it can be set to any value between 1 and 9,007,199,254,740,992 (the largest integer that can be stored in an IEEE 64-bit float).

Defeating Parameter Restrictions

By default, number and natural parameter restrictions are enabled, but they can be turned off. Simply provide a CF::Impure configuration declaration to turn checking on or off.

CF::Impure = 1  // Turn purity restrictions off
CF::Impure = 0  // Turn purity restrictions on

Reusing Parameters

In the curve example above the curve rule invokes itself recursively using the same parameters. There is a shorthand for doing this common operation: put an equal sign, =, in place of the parameters in the shape invocation. This works for recursive shape invocation and also for invoking a different shape that has the same parameter types. Using the parameter reuse syntax is optional. This curve shape draws exactly the same as the other one:

shape curve(number shrink, number turn) {
  curve(=) [[y 0.5 r turn s shrink y 0.5]]

But using the parameter reuse syntax makes your code simpler, more expressive, and faster.

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.