In addition to the predefined math functions, a CFDG file can declare new functions. User functions are declared at the global level, not within shapes or paths. The syntax:
type name(parameters) = expression
Where name is the function name, parameters are the function parameters, and expression is the expression that is evaluated for the function. If the function has no parameters then the function name must still be followed by empty parentheses to distinguish a function declaration from a variable declaration.
The parameters are just like the parameters for a shape or path: a comma-separated list of parameter types and parameter names. The return type and the parameters types can be any of the supported types: number, vector, natural, adjustment or shape. The type keyword can be omitted if the return type or parameter type is number.
erf(n) = if(n > 0, 1, -1) * sqrt(1 - exp(-n * n * (4 / 3.1415926535 + 0.147 * n * n) / (1 + 0.147 * n * n)))
Recursive and mutually recursive functions are allowed:
// Fibonacci function
fib(n) =
if(n <= 1,
n,
fib(n-1) + fib(n-2)
)
// Ackerman function
ack(m, n) =
if(m == 0,
n+1,
if(n == 0,
ack(m-1, 1),
ack(m-1, ack(m, n-1))
)
)
Note that these user functions use the if function, not the if control flow element.
Example of non-numeric parameter and return types:
// Complex math operations
vector2 Cadd(vector2 lhs, vector2 rhs) = lhs[0]+rhs[0],lhs[1]+rhs[1]
vector2 Csub(vector2 lhs, vector2 rhs) = lhs[0]-rhs[0],lhs[1]-rhs[1]
vector2 Cmult(vector2 lhs, vector2 rhs) = lhs[0]*rhs[0] - lhs[1]*rhs[1],lhs[1]*rhs[0] + lhs[0]*rhs[1]
vector2 Cdiv(vector2 lhs, vector2 rhs) = let(vsq = rhs[0]*rhs[0]+rhs[1]*rhs[1];
(lhs[0]*rhs[0] + lhs[1]*rhs[1])/vsq,
(lhs[1]*rhs[0] - lhs[0]*rhs[1])/vsq)