Scripting language tutorial - nextbreakpoint/nextfractal GitHub Wiki
NextFractal is currently able to interpret two scripting languages: Mandelbrot and CFDG. Mandelbrot is a domain specific language for creating images of the Mandelbrot set (and its variants, such as Julia and Fatou sets). CFDG is a context-free grammar for creating images by an iterative process.
Each script begins with a fractal block which contains two mandatory blocks, orbit and color. The orbit block defines the algorithm to be used for computing the orbit for each point of the complex plane, or pixel of the screen if you prefer. The color block defines the algorithm to be used for computing the color of the point or pixel. Each one of those blocks have its own isolated variables scope, but we can export variables from orbit's scope to color's scope. Typically we export the right amount of variables required to compute the color (to limit the amount of memory required to process the image).
fractal {
orbit [...] [...] {
...
}
color [...] {
...
}
}
The orbit block requires two list of parameters and it contains one or more trap blocks, and not more than one of begin, loop, and end blocks. The blocks begin and end are optional. The block begin defines statements which are executed once at the beginning of the computation. The block end defines statements which are executed once at the ending of the computation. The block loop defines statements which are executed repeatedly until some conditions are met. The first list of parameters must contain two vectors <x,y> representing points x+yi of the complex plane. The second list must contains all state variables which need to be exported from orbit's scope to color's scope. The state variables are cached in memory to avoid recomputing all orbits when only color block changes. Beware that memory usage might exceed the available memory for a large number of variables.
orbit [<-3.0,-1.5>,<0.0,1.5>] [x,n] {
trap name [...] {
}
...
begin {
...
}
loop [...] (...) {
...
}
end {
...
}
}
The begin block is optional and it might contain assignment statements, such as variable=expression, or if-else statements. The assignment statements can be used to declare new variables in orbit's scope. There are few implicit variables, x, w and n, which are declared and initialized automatically according to fractal's type, Mandelbrot or Julia/Fatou. The state variable x is initialized with the initial state or the current point, the constant w is initialized with the current point or Julia/Fatou constant, and n is always initialized with zero.
begin {
y = |x|;
z = <x>;
...
}
The loop block is mandatory and it requires a list of parameters and a termination condition. The list of parameters must contain the initial value of index variable n and the number of iterations. The termination condition must contain a logical expression which is used to terminate the iteration loop. The loop terminates when the confition is true or when n is equals to the number of iterations. In addition, the loop can be terminated using a stop instruction. The loop must contain assignment statements, such as variable=expression, or if-else statements. The assignment statements can be used to declare temporary variables which will be visible only in loop's scope.
loop [0,100] (|x| > 2) {
x = x * x + w;
...
}
loop [0,200] (|x| > 4) {
q = 2.5;
x = x ^ q + w;
...
}
The end block is optional and it might contain assignment statements, such as variable=expression, or if-else statements. The assignment statements can be used to declare temporary variables. After all statements are executed, the final value of variables which are declared in state variables parameters list of orbit block are cached and then exported to color's scope.
end {
q = y + z;
t = sin(n * pi / 10 + q);
...
}
The trap block is optional, it requires a list of parameters and it must contain a sequence of commands. The list of parameters must contain the initial location of trap in the complex plane. The commands define the shape of a polygon in the complex plane which can be used in any expression to test if a complex number belong to the area delimited by the polygon. Command's parameters must be constant, we can't use variables or expressions.
Command | Description |
---|---|
MOVETO(p) | Move origin using absolute destination p=<px,py> |
MOVEREL(d) | Move origin using relative distance vector d=<dx,dy> |
LINETO(p) | Add line using absolute destination p=<px,py> |
LINEREL(d) | Add line using relative distance vector d=<dx,dy> |
ARCTO(p1,p2) | Add arc using absolute destination p1 and control point p2 |
ARCREL(d1,d2) | Add arc using relative distance vector d1 and control vector d2 |
QUADTO(p1,p2) | Add quad curve using absolute destination p1 and control point p2 |
QUADREL(d1,d2) | Add quad curve using relative distance vector d1 and control vector d2 |
CURVETO(p1,p2,p3) | Add spline curve using absolute destination p1 and control points p2 and p3 |
CURVEREL(d1,d2,d3) | Add spline curve using relative distance vector d1 and control vectors d2 and d3 |
CLOSE | Close polygon connecting last point to initial point |
trap circle [<0,0>] {
MOVETO(<1,0>);
ARCTO(<1,1>,<0,1>);
ARCTO(<-1,1>,<-1,0>);
ARCTO(<-1,-1>,<0,-1>);
ARCTO(<1,-1>,<1,0>);
}
trap rectangle [<0,0>] {
LINETO(<0,1>);
LINETO(<1,1>);
LINETO(<1,0>);
LINETO(<0,0>);
}
trap triangle [<0,0>] {
MOVETO(<0,1>);
LINETO(<1,0>);
LINETO(<-1,0>);
CLOSE;
}
The color block requires a list of parameters and it contains not more than one init block, and one or more of palette and rule blocks. The list of parameters must contain a tuple (a,r,g,b) representing four color components, or a hexadecimal number #AARRGGBB representing a 32bits integer value, or a hexadecimal number #RRGGBB representing a 24bits integer value.
color [(1,1,1,1)] {
init {
...
}
palette {
...
}
...
rule (...) {
...
}
...
}
The init block is optional and it might contain assignment statements, such as variable=expression, or if-else statements. The assignment statements can be used to declare new variables in color's. The color's scope contains implicit state variables which are imported from orbit's scope. Those state variables usually contain all the information required for computing the pixel's color.
init {
y = |x|;
z = <x>;
...
}
The palette block is optional, it requires a name and it must contain one or more interpolation sequences. A sequence is defined as a list which contains range, length, and an optional interpolation function. A palette defines a table of colors which can be used to produce color values. The colors are generated interpolating color values from the inital value to the final value of the range, using a linear interpolation or a user defined interpolation function if present. The total size of the palette is equals to the sum of the length of all sequences. The colors can be defined as a tuple (a,r,g,b) representing four color components, or a hexadecimal number #AARRGGBB representing a 32bits integer value, or a hexadecimal number #RRGGBB representing a 24bits integer value.
palette MyPalette {
[#FFFF0000 > #FFFFFFFF, 100];
[#FFFFFFFF > #FF000000, 100];
...
}
palette MyPalette {
[(1,0,0,0) > (1,1,0,0), 200, sin(x)];
...
}
The rule block is optional, it requires an activation condition and an opacity level, and it must contains either a single value or a tuple of three or four values. The values must be real numbers in interval [0,1]. The single value s is interpreted as tuple 1,s,s,s. The tuple with three values a,b,c is interpreted as tuple 1,a,b,c. The tuple represents four color components a,r,g,b. As special case, a rule may create a tuple directly from a palette using the notation name[i]. The initial value of a pixel's color is equals to background color declared in color block. The final color is computed applying all the active rules and composing the output of each rule sequentially according to the opacity level.
rule (n > 0) [0.8] {
MyPalette[n]
}
rule (n > 0) [1.0] {
c
}
rule (|x| > 4) [0.9] {
r,g,b
}
rule (|x| > 4) [0.9] {
a,r,g,b
}
A logical expression is an expression which has a boolean value, true or false. It might contains a contant, a variable, or a composition of logical operators and other expressions. We can use parenthesis to create nested expressions and control operators priority. There is one implicit boolean constant, julia, which is initialized according to the type of algorithm in use, false for Mandelbrot or true for Julia/Fatou.
Operator | Description |
---|---|
x = y | true if x is equals to y, false otherwise |
x < y | true if x is less than y, false otherwise |
x > y | true if x is greather than y, false otherwise |
x <= y | true if x is less than y or equals to y, false otherwise |
x >= y | true if x is greather than y or equals to y, false otherwise |
x <> y | true if x is not equals to y, false otherwise |
x & y | true if x is true and y is true, false otherwise |
x | y | true if x is true or y is true, false otherwise |
x ^ y | true if x is true and y is false or x is false and y is true, false otherwise |
~x | true if x is false, false otherwise |
loop [...] (|x| > 2 | circle ? x) {
...
}
init {
if (|z| > 1 & |z| < 10) {
...
} else {
...
}
}
rule (|x| > 4) [...] {
...
}
rule ((|x| > 4 & |x| < 8) | n > 10) [...] {
...
}
An arithmetic expression has a numeric value, real or complex. It might contain a constant, a variable, a function, or a composition of arithmentic operators and other expressions. We can use parenthesis to create nested expressions and control operators priority. The type of the expression is statically defined according to the type of constants, variables, or functions used. There are built-in operators, functions, and constants which can be used in any arithmetic expression. We can declare a variable using the vector notation <x,y> or we can use the complex number notation x+yi, where x and y are constants or arithmetic expressions.
Function | Description |
---|---|
pha(x) | Phase of x |
mod(x) | Modulus of x |
mod2(x) | Modulus ^ 2 of x |
re(x) | Real part of x |
im(x) | Imaginary part of x |
cos(x) | Cosine of x |
sin(x) | Sine of x |
tan(x) | Tangent of x |
acos(x) | Acosine of x |
asin(x) | Asine of x |
atan(x) | Atangent of x |
log(x) | Logarithm of x |
exp(x) | Exponential of x |
sqrt(x) | Square root of x |
abs(x) | Absolute value of x |
ceil(x) | Closet integer bigger than x |
floor(x) | Closet integer smaller than of x |
Function | Description |
---|---|
pow(x,y) | x ^ y |
atan2(y,x) | Atan2 of y and x |
hypot(x,y) | x ^ 2 + y ^ 2 |
max(x,y) | if x > y x else y |
min(x,y) | if x < y x else y |
square(x) | Square signal of period 1 |
saw(x) | Saw signal of period 1 |
ramp(x) | Ramp signal of period 1 |
pulse(x,w) | Pulse signal of period 1 and width w |
time() | Relative time in seconds |
Operator | Description |
---|---|
|x| | Modulus of x |
<x> | Phase of x |
x + y | Addition x + y |
x - y | Subtraction x - y |
x * y | Multiplication x * y |
x / y | Division x / y |
x ^ y | Power y of x |
Constant | Description |
---|---|
e | Euler constant |
pi | Pythagoras constant |
2pi | 2 * pi |
You can find a comprehensive documentation about the CFDG language on www.contextfreeart.org.