MbLan is the app's embedded expression language used for defining custom fractal types and paint modes.
For a simple fractal type or paint mode definition, you use only MbLan expressions. If you have any experience with programming, or even with spreadsheet formulas, you should find it easy.
For an advanced definition you use MbLan programs.
The MbLan syntax is based on C/Java.
Do not forget to end each instruction with a semicolon ;
In addition to C/Java syntax, the MbLan introduces some extensions making it easier to enter formulas using an Android keyboard.
The MbLan compiler is powered by JavaCC. Those who are really interested may take a look at MbLan syntax definition file: mblan.jj.
The MbLan uses the following data types:
int | integer number (standard C/Java int) |
double | floating-point number (standard C/Java double) |
complex | a complex number using doubles for the real and imaginary part |
You may refer to real and imaginary parts of a complex variable z
or a complex expression as follows:
real part |
z.x |
(z + c).x |
imaginary part |
z.y |
(z + c).y |
You may construct complex values using complex function or complex literals as in the following examples:
complex(z.x - 1, z.y + 1)
complex(1, 0)
1.2 + 2.3e-2i
-5i
You may declare helper variables at the very beginning of the Initialization code section of an advanced custom fractal type or paint mode definition. Follow the example below:
double sum, tmp4me;
int count;
complex a, a_c;
int k;
Helper variables are by default initialized to 0.
Because of the shortcut power syntax extension, variable names must not end with a digit.
You may use the following C/Java based syntax:
Arithmetic operators:
The MbLan supports arithmetic operations on complex or mixed-type arguments (except for % operator). The implicit type conversion from int to double and from double to complex is also supported. For explicit type conversion see the function reference. Integer division by zero will give the result of 0 instead of crashing the app. |
Comparison operators:
The result of comparison is int value 0 (false) or 1 (true). When comparing complex numbers the result is 1 if both parts fulfill the condition.
No support for chaining like |
Logical operators:
Logical operators take int arguments (0 means false, non-zero value means true) and give int result of 0 or 1. |
Unary arithmetic negation:
|
Unary logical negation:
|
Conditional expression:
|
Function call:
|
Comments:
|
The MbLan introduces the following syntax extensions to make formulas more readable and easier to type on a mobile device.
You may express multiplication by writing arguments with no *
operator between.
You may even write no space between arguments (of course not if both are variables).
Warning: implicit multiplication binds stronger than explicit one (see the last example below). More details in Operators precedence section.
example use | equivalent to |
a b c |
a * b * c |
3.5zc |
3.5 * zc |
2z + z2 |
2 * z + z^2 |
2z + 2(z - c) |
2 * z + 2 * (z - c) |
(k + 1)(z - 2) |
(k + 1) * (z - 2) |
z (-2) |
z * -2 |
a b / c d |
(a * b) / (c * d) |
Warning: exponentiation binds stronger than implicit multiplication (see the last example below). More details in Operators precedence section.
example use | equivalent to |
z^3 |
pow(z, 3) |
c^0.7 |
pow(c, 0.7) |
(a + b)^3^(k + 1) |
pow(a + b, pow(3, k + 1)) |
z^2c |
pow(z, 2) * c |
You may express exponentiation by following a base (variable only) with an exponent (positive integer literal only) with no space between. Typing ^ on a mobile device is not so easy.
This is the reason that variable names must not end with a digit.
example use | equivalent to |
z2 |
pow(z, 2) |
a4 |
pow(a, 4) |
z 4 |
z * 4 |
z-2 |
z - 2 |
(z + 1)5 |
(z + 1) * 5 |
You may call unary functions without using parenthesis. This can be especially useful for chaining function calls like writing:
abs log rad z
instead of:
abs(log(rad(z)))
Warning: unary function call binds stronger than any other operation but shortcut power (see examples below). More details in Operators precedence section.
example use | equivalent to |
abs z |
abs(z) |
abs sin z^2 |
(abs(sin(z)))^2 |
sin(z)^2 |
(sin(z))^2 |
log abs x y |
log(abs(x)) * y |
sin z.x |
(sin(z)).x |
sin 2z cos z2 |
sin(2) * z * cos(z^2) |
The MbLan operators precedence in descending order (just like in C or Java plus MbLan extensions).
shortcut power | z2 |
unary function call | sin z |
complex postfix | z.x z.y z.re z.im |
exponentiation | ^ |
implicit multiplication | 2z |
unary operators | - ! |
multiplicative operators | * / % |
additive operators | + - |
relational operators | < <= => > |
comparison operators | == != |
logical AND | && |
logical OR | || |
conditional expression | ? : |
The operators associativity is like in C or Java. Exponentiation operator ^
binds right-to-left.
In case of any doubts, you may use parenthesis.
An MbLan program is a sequence of instructions. Each instruction must be followed by a semicolon ;
You may also use instruction blocks { }
like in C or Java.
The list of available instructions is quite short:
assignment
|
iteration break
|
conditional instruction
|
The following example is iteration code for a hybrid Mandelbrot / Burning ship fractal with a custom escape condition:
if (abs(z.x) >= bailout) break;
if (n % 2) {
/* two steps of Burning ship without adding c */
z = (abs z)^2;
z = (abs z)^2;
}
else
/* one step of Mandelbrot */
z = z2 + c;
The result of the above example using Stings paint mode (no smoothing defined).
You may use the built-in variables listed below. Any change of value affects only the calculation for the current point.
c |
complex the current point read-only |
z |
complex the current orbit element value read-only in a paint mode definition |
limit |
int the iteration limit read-only accessible in a fractal definition the value may vary between points when using the Detail level control as the app may adjust the iteration limit while rendering the image |
i |
int the current orbit element index read-only accessible in iteration code of a paint mode or a paint mode formula |
n |
int the last computed orbit element number in the fractal iteration code section the orbit length in a paint mode definition or in the fractal finalize code section can be written only in fractal finalize code |
smooth |
double the value of the smooth factor can be written only in the finalize code section of an advanced fractal definition always equal 1 when computing a custom paint mode for the fractal interior |
exponent |
double the value of the exponent used for smoothing when using the DIVERGENCE escape accessible only in fractal definition |
bailout |
double the value of bailout used for the DIVERGENCE escape or the CONVERGENCE escape accessible only in fractal definition |
value |
double the paint value to be mapped to the color from the palette accessible only in the iteration and the finalize code sections of an advanced paint mode definition |
start |
int the index of starting element for orbit processing when computing a paint value (see Orbit based coloring section) accessible only in a paint mode definition can be written only in the initialization code section |
pi |
double read-only Pi value |
e |
double read-only natural logarithm base |
You may refer to the orbit elements indexing built-in z
variable like an array.
Follow the examples listed below.
z[0] |
the first element |
z[expr] |
an arbitrary element where expr is an int expression |
z[n] |
the previous element in a fractal formula or iteration code
where this is equivalent to |
z[n - 1] |
the element preceding a previous one in fractal formula or iteration code the last element in a paint mode definition or fractal finalize code |
z[i] |
the current element in a paint mode definition where
this is equivalent to |
z[i - 1] |
a previous element in a paint mode definition |
You do not need to worry about exceeding the orbit range. You will just get 0 value in such a case (even if you use a negative index).
The following sections describe all the functions available in the MbLan with their supported arguments data types and the result data type.
The MbLan supports implicit data type conversion so you may pass an int instead of a double or a double instead of a complex.
Most of the functions work like their C/Java equivalents so they are not explained in detail.
WARNING: Due to RenderScript limitations, exponentiation, logarithm and trigonometric functions work on single-precision floating-point numbers. This may cause image quality loss at a zoom of about 1e+5 or greater.
int |
int(double) : int type conversion |
double |
double(int) : double type conversion |
complex |
complex(double, double) : complex complex(x, y) = x + i y |
round |
round(double) : double |
floor |
floor(double) : double |
ceil |
ceil(double) : double |
abs |
abs(int) : int abs(double) : double abs(x) = |x| |
sgn |
sgn(int) : int sgn(double) : double sgn(x) = (x == 0 ? 0 : (x > 0 ? 1 : -1)) |
min |
min(int, int) : int min(double, double) : double |
max |
max(int, int) : int max(double, double) : double |
undef |
undef(double) : int undef(complex) : int returns 1 if an argument is undefined (NaN or infinity) and 0 otherwise for complex argument returns 1 if any of parts is undefined |
abs |
abs(complex) : complex abs(x + i y) = |x| + i |y| |
rabs |
rabs(complex) : complex rabs(x + i y) = |x| + i y |
iabs |
iabs(complex) : complex rabs(x + i y) = x + i |y| |
rad |
rad(complex) : double rad(z) = |z| = sqrt(z.x^2 + z.y^2) |
rad2 |
rad2(complex) : double rad2(z) = |z|^2 = z.x^2 + z.y^2 |
arg |
arg(complex) : double complex number argument (value in the range of (-pi, pi]) |
conj |
conj(complex) : complex conj(x + i y) = x - i y |
flip |
flip(complex) : complex flip(x + i y) = -x + i y |
swap |
swap(complex) : complex swap(x + i y) = y + i x |
dot |
dot(complex, complex) : double dot(a, b) = a.x b.x + a.y b.y |
min |
min(complex, complex) : complex min(a, b) = min(a.x, b.x) + i min(a.y, b.y) |
max |
max(complex, complex) : complex max(a, b) = max(a.x, b.x) + i max(a.y, b.y) |
sqr |
sqr(int) : int sqr(double) : double sqr(complex) : complex sqr(x) = x^2 |
sqrt |
sqrt(double) : double sqrt(complex) : complex square root, sqr(x) = x^0.5 |
pow |
pow(double, int) : double pow(double, double) : double pow(complex, int) : complex pow(complex, double) : complex pow(complex, complex) : complex |
log |
log(double) : double log(complex) : complex natural logarithm |
exp |
exp(double) : double exp(complex) : complex exp(x) = e^x |
sin |
sin(double) : double sin(complex) : complex |
cos |
cos(double) : double cos(complex) : complex |
tan |
tan(double) : double tan(complex) : complex |
sinh |
sinh(double) : double sinh(complex) : complex |
cosh |
cosh(double) : double cosh(complex) : complex |
tanh |
tanh(double) : double tanh(complex) : complex |
asin |
asin(double) : double |
acos |
acos(double) : double |
asin |
asin(double) : double |
atan |
atan(double) : double |
asinh |
asinh(double) : double |
acosh |
acosh(double) : double |
atanh |
atanh(double) : double |
lyapstep |
lyapstep(AB_sequence, complex, complex) : complex |
The special lyapstep function is a shortcut for computing Lyapunov fractals.
Its first argument should be a sequence of A and B characters, eg.:
lyapstep(AAABB, z, c)
The following pseudo-code describes how the function works.
lyapstep(seq, z, c) {
double r;
if (seq[n % len(seq)] == 'B')
r = -c.y;
else
r = c.x;
double a = abs(r * (1 - 2 * z.x));
z.y = (a > 0 ? -log(a) : 0);
z.x = r * z.x * (1 - z.x);
return z;
}
An example of use can be found in built-in Lyapunov fractals definitions.
You may get undefined (NaN or infinity) variable values (eg. as a result of double division by zero or computing a square root for a negative argument). In such a case the computing will not crash but you may get further undefined values.
When an orbit element value is undefined the escape condition will never be met and iteration will continue until iteration count limit is reached.
If a paint value is undefined it is skipped by a built-in aggregation. When using CUSTOM aggregation, a final paint value is changed to zero if it is undefined.
Warning: for simplicity, handling undefined values is not shown in a visible fixed program part of an advanced paint mode definition.
You may handle undefined values by yourself using the built-in undef
function.
The MbLan compiler does not perform any code optimization. You may do it yourself by using helper variables for common intermediate results. Do not expect too much unless you have really big common expressions in your fractal definition.
Let's consider the following iteration code of root finding fractal:
z = z - (z4 + z3 - 1) / (4z3 + 3z2);
It will compute a little bit faster if you rewrite it like this:
zb = z2;
zc = zb z;
z = z - (zb2 + zc - 1) / (3zc + 3zb);
You may also use initialization code to compute intermediate values that do not depend on previous orbit value. This may give noticeable effects if there are many such calculations. An example may be found in the built-in Magnet 2 fractal definition.