Skip to content

Expressions

John Horigan edited this page May 22, 2017 · 5 revisions

Context Free allows expressions to be used anywhere that the compiler accepts a number, except for rule/path weights.

The following components are allowed in an expression:

  • constants
    • simple numeric constants (e.g., 12.45)
    • percentages (e.g., 9.6%)
  • functions
  • vector[index] : vector access
  • (expression) : parentheses
  • ^ : exponentiation
  • - : negation
  • * / : multiplication and division
  • + - -- : addition, subtraction, and proper subtraction
  • .. … +- ± : random number operators
    • x .. y or xy is equivalent to rand(x, y)
    • x +- y or x ± y is equivalent to rand(x-y, x+y)
  • < > <= ≤ >= ≥ == <> ≠ : comparison
  • && || ^^ : boolean and, or, and exclusive-or
  • , : vector creating, binds several numeric expressions to form a vector

Standard rules of operator ordering and precedence are supported; i.e., 2 + 3 * 4 is equivalent to 2 + (3 * 4).

Unicode Support

Several operators have ASCII forms and Unicode forms. They are interchangeable and Context Free has keyboard shortcuts for entering them. Cfdg files are always UTF-8 encoded. The operator pairs are:

Operator ASCII Unicode
Random range ..
Random range +- ±
Less than or equal to <=
Greater than or equal to >=
Not equal to <>
Infinity infinity()

Functions

Trigonometry functions:

  • cos(x) - cosine of x in degrees
  • sin(x) - sine of x in degrees
  • tan(x) - tangent of x in degrees
  • acos(x) - arc-cosine of x, returned in degrees
  • asin(x) - arc-sine of x, returned in degrees
  • atan(x) - arc-tangent of x, returned in degrees
  • atan2(y, x) - arc-tangent of y/x, returned in degrees

Hyperbolic functions:

  • cosh(x) - hyperbolic cosine of x
  • sinh(x) - hyperbolic sine of x
  • tanh(x) - hyperbolic tangent of x
  • acosh(x) - hyperbolic arc-cosine of x
  • asinh(x) - hyperbolic arc-sine of x
  • atanh(x) - hyperbolic arc-tangent of x

General math functions:

  • log(x) - natural logarithm of x
  • log10(x) - decimal logarithm of x
  • exp(x) - ex
  • sqrt(x) - square root of x
  • abs(x) - absolute value of x
  • mod(x, y) - x modulo y

Integer functions:

  • floor(x) - rounds x to the next lower integer
  • factorial(x) - x!
  • sg(x) - returns 0 if x=0 or 1 if x≠0
  • isNatural(x) - returns true if x is a legal natural number, and integer in the interval [0,CF::MaxNatural]
  • div(x, y) - x÷y in the integer domain
  • divides(x, y) - return 1 if y divides x or 0 if y does not divide x

Binary functions:

  • bitnot(x) - binary inverse of x
  • bitor(x, y) - binary OR of x and y
  • bitand(x, y) - binary AND of x and y
  • bitxor(x, y) - binary exclusive-OR of x and y
  • bitleft(x, y) - binary left shift of x by y bits
  • bitright(x, y) - binary right shift of x by y bits

The binary functions convert their operands to 52-bit, unsigned binary numbers before performing the binary operation. Results are masked by 0xfffffffffffff.

Miscellaneous functions:

  • infinity() - ∞
  • infinity(x) - ∞ if x≥0 or -∞ if x<0
  • min(x0, x1, x2, …) - evaluates arguments and returns the smallest one
  • max(x0, x1, x2, …) - evaluates arguments and returns the largest one

Animation frame time functions:

  • frame() - current animation frame number or CF::Frame if not animating, the return value is 0 for the first frame through 1 for the last frame.
  • ftime() - current animation time or CF::FrameTime if not animating

Random functions:

Function Description
rand_static() Static random number in the interval [0,1), uniform distribution
rand_static(x) Static random number in the interval [0,x) (if x > 0) or [x,0) (if x < 0), uniform distribution
rand_static(x, y) Static random number in the interval [x,y) (if y > x) or [y,x) (if x > y), uniform distribution
rand() Random number in the interval [0,1), uniform distribution
rand(x) Random number in the interval [0,x) (if x > 0) or [x,0) (if x < 0), uniform distribution
rand(x, y) Random number in the interval [x,y) (if y > x) or [y,x) (if x > y), uniform distribution
rand::normal(mean, stddev) Generates random numbers according to the Normal (or Gaussian) random number distribution
rand::lognormal(mean, stddev) Generates random numbers according to the Log-Normal random number distribution
rand::exponential(rate) Generates random non-negative floating-point values x, distributed according to and exponential probability density function
rand::gamma(alpha_shape, beta_scale) Generates random numbers according to the gamma random number distribution
rand::weibull(alpha_shape, beta_scale) Generates random numbers according to the weibull random number distribution
rand::extremeV(location, scale) Generates random numbers according to the extreme value random number distribution
rand::chisquared(degree_freedom) Generates random numbers according to the chi squared random number distribution
rand::cauchy(location, scale) Generates random numbers according to the Cauchy random number distribution
rand::fisherF(m_degree_freedom, n_degree_freedom) Generates random numbers according to Fisher's F-distribution
rand::studentT(degree_freedom) Generates random numbers according to Student's T-distribution
randint() Random integer in the interval [0,2) (i.e., returns 0 or 1), uniform distribution
randint(x) Random integer in the interval [0,x) (if x > 0) or [x,0) (if x < 0), uniform distribution
randint(x, y) Random integer in the interval [x,y) (if y > x) or [y,x) (if x > y), uniform distribution
randint::bernoulli(probability) Generates random boolean value with a specified probability of being true
randint::binomial(trials, probability) Generates random non-negative integer values i, distributed according to a binomial distribution
randint::negbinomial(trial_failures, probability) Generates random non-negative integer values i, distributed according to a negative binomial distribution
randint::geometric(probability) Generates random non-negative integer values i, that represents the number of yes/no trials which are necessary to obtain a single success
randint::poisson(mean) Generates random non-negative integer values i, distributed according to a Poisson distribution
randint::discrete(weight_0, weight_1, … , weight_n) Generates random integer in the range [0,n], where each value i appears with a probability according to weight_i

The rand_static() functions are converted into a random number when the cfdg file is compiled. So the value is constant for the entire run, but it is different for each variation and for each instance of rand_static() in the cfdg file. A rand_static() function inside of a loop has the same value for each iteration of the loop. This is not as useful as the full dynamic random function that everyone fervently desires. But it is the best that can be done with the Context Free 2.x execution model and it is moderately useful.

rand() and randint() (and the distribution functions) return new values each time they are executed. randint() is equivalent to randint::bernoulli(0.5) and is provided as a convenience.

Special Functions

  • select(n, expr0, expr1, expr2, expr3, …) - evaluates n and then evaluates and returns expr0 if n<1, expr1 if 1≤n<2, expr2 if 2≤n<3, etc. Must have at least two arguments. The expr0, expr1, expr2, etc. need not be numeric expressions, they can all be vectors, naturals, shape adjustments, or shape specifications. They must all be the same type.
  • if(n, expr_true, expr_false) - evaluates n and then evaluates and returns expr_true if n≠0 or expr_false if n=0. This is just syntactic sugar for the select() function.
  • let(var1=expr1; var2=expr2; … ; expression) - evaluates each of the argument expressions, expr1, expr2, etc., and binds them to var1, var2, etc. Then it evaluates the last expression in the context of the bound variables and returns this value. NB: the variable bindings are separated by semicolons, not commas. The scope of each variable is the variable bindings that follow it as well as the last expression.

Let() Examples

shape test {
  // let() returns a vector2, which is used in a size adjustment
  CIRCLE[s let(n=5…6;m=7…8;n,m) a -0.5 sat 1 b 1 y 5 h 90]
 
  // let() returns a shape adjustment, which is inserted into another shape adjustment
  CIRCLE[trans let(n=5…6;m=7…8;[s n m x (2.5 + 0.5 * n)]) a -0.5 sat 1 b 1]
 
  // Use lat to specify a shape and then draw with it
  draw = let(n = randint(3); select(n, CIRCLE, SQUARE, TRIANGLE))
  draw[s 5 7 a -0.5 sat 1 b 1 x -5 h 180]
 
  // Directly use let() function as a shape specifier
  let(n = randint(3); select(n, CIRCLE, SQUARE, TRIANGLE))[s 5 7 a -0.5 sat 1 b 1 y -5 h 270]
}

Let us know if there is a function that you would like to see added to Context Free.

startshape foo
 
path trill {
    MOVETO(cos(234), sin(234))
    loop 5 [r -144] 
        CURVETO(0, 1, cos(234) + cos(324), sin(234) + sin(324), 1, 1)
    CLOSEPOLY(CF::Align)
    FILL(CF::EvenOdd)[]
    STROKE[a -0.5]
}
 
shape foo {
    trill[]
}

Simple Expressions

Inside shape adjustments there are limitations placed on the types of expressions that are allowed. Expressions in shape adjustments are called simple expression, while the full strength expressions described above are called ordinary expressions.

  • numeric constant
    • simple numeric constants (e.g., 12.45)
    • percentages (e.g., 9.6%)
  • (ordinary expression)
  • -simple expression
  • +simple expression
  • variable
  • function(ordinary expression) or function()
  • vector[index] : vector access
  • simple expressionsimple expression or simple expression .. simple expression
  • simple expression ± simple expression or simple expression +- simple expression

You can insert an ordinary expression into a simple expression context by wrapping it with parentheses.

You can’t perform that action at this time.