Ultra Fractal bringt von sich aus schon eine Vielzahl an Standardformeln mit. Darüber hinaus können zahlreiche Formeln aus dem Internet geladen werden. Doch irgendwann wollt Ihr Eure eigenen Ideen umsetzen oder einfach nur experimentieren.

Mit diesem Tutorial möchte ich Euch zeigen, wie Ihr mit Hilfe des Formel-Editors Eure eigenen Formeln entwickeln könnt. Je nach Aufgabentyp wird zum Berechnen von Fraktalen zwischen folgenden Formeldateien unterschieden:

  • Fraktalformeln .ufm (fractal formula files),
  • Kolorierungsalgorithmen .ucl (coloring algorithm files) sowie
  • Transformationen .uxf (transformation files).

Aufgrund dieses Aufgabentyps gliedert sich das Tutorial in folgende Teile:

  1. Grundlegende Konventionen (Ausdrücke, Variablen, Typen, Schleifen etc.),
  2. Erstellen von Fraktalformeln,
  3. Erstellen von Kolorierungsalgorithmen sowie
  4. Erstellen von Transformationen.

Weitergehende Informationen bekommt Ihr in der Ultra Fractal Hilfe.

Grundlegende Konventionen

Wie in der Umgangssprache gelten für eine Programmiersprachen gewisse grammatikalische Regeln. Wenn Ihr diese Regeln ignoriert, dann ist der Compiler nicht in der Lage, die Anweisungen der Formeldatei korrekt auszuführen und gibt Euch eine Fehlermeldung aus.

Ausdrücke

Einige Abschnitte (init und loop) können Anweisungen in Form von Ausdrücken enthalten. Darüber hinaus können auch Variablen, Parameter und Konstanten einen Ausdruck darstellen. Die Kombination von Operatoren und Werten bezeichnet man als Ausdrücke.

Ausdrücke können in Programmen an verschiedenen Stellen verwendet werden:

  • auf der rechten Seite von Zuweisungen
  • z = z^@power + #pixel

  • im Aufruf von Funktionen
  • e1 = sqrt(2 * log(cabs(z1)) * cabs(z1) / cabs(o1)) * @zscale

  • in Bedingungen
  • if(real(z2) > 0.0)

Datentypen und Datentypenkompatibilität

Die oben beschriebenen Ausdrücke können vom verschiedenen Typ sein. In Ultra Fractal gibt es folgende Typen:

b o o l

Wenn zwei Ausdrücke miteinander verglichen werden, so können sie nur zwei Werte annehmen: true bzw. false

3 < 4 ; true

i n t

Die Integerwerte liegen Werte im Bereich von -2.147.483.648 bis 2.147.483.647 und sie werden zum Zählen sowie für arithmetische Operationen benötigt.

-3, 2

f l o a t

Mithilfe von Fließkommazahlen können gebrochene Zahlen dargestellt werden; ihre Präzision ist praktisch unbegrenzt.

-3.734, 2.5e8

c o m p l e x

Komplexe Zahlen werden durch komplexe Ausdrücke repräsentiert, die aus zwei Fließkommazahlen bestehen. Sie ermöglichen fraktale Berechnungen.

(1.4, 3) = 1.4+3i

c o l o r

Mit diesem Ausdruck, der eine Farbe repräsentiert, können Operationen durchgeführt und in color-Variablen gespeichert werden. Eine Farbe wird entsprechend den Farbkomponenten Rot, Grün, Blau und Alpha-Kanal (Opazität) intern aus vier Fließkommazahlen zusammengesetzt. Die Werte liegen zwischen 0 und 1. Dieser Typ ist nur für direkte Kolorierungsalgorithmen vorgesehen.

Wenn zwei Ausdrücke unterschiedlichen Datentyps miteinander berechnet werden, dann erhält das Ergebnis den höherwertigen Datentyp der Einzelausdrücke.

Im folgenden Beispiel ist das Ergebnis von 3 + 2.1 vom Typ float, da der Wert 2.1 (float) höherwertiger ist, als der Wert 3 (int). Der Datentyp complex ist höherwertiger als der Typ float.

Der Compiler kann nur in höhere Datentypen umwandeln. Das folgende Beispiel würde in eine Fehlermeldung münden:

int i = 3 / 2.

Fehlermeldung des Compilers bei falschem Datentyp

Konstanten

Boole’sche Konstanten

Sie sind vom Typ ‘bool‘ und es gibt zwei Konstanten: true und false.

Integer-Konstanten

Sie sind vorzeichenbehaftete Zahlen vom Typ ‘int‘ und liegen im Bereich von -2.147.483.648 bis 2.147.483.647.

-23

Fließkomma-Konstanten

Sie sind vom Typ ‘float‘ und bestehen einer vorzeichenbehafteten Mantisse, optional gefolgt von einem ‘E‘ sowie einem vorzeichenbehafteten Exponent, der die Zehnerpotenz darstellt. Der Exponent liegt im Bereich von -4.931 bis 4.931.

98.283E-3 ; 0,098283

Komplexe Konstanten

Sie sind vom Typ ‘complex‘ und bestehen aus zwei in Klammern stehenden Fließkommazahlen, die durch ein Komma voneinander getrennt sind. Imaginäre Zahlen können alternativ durch ein dahinterstehendes i gekennzeichnet werden.

2 + 1.63i ; (2, 1.63)

Farb-Konstanten

Sie sind vom Typ ‘color‘ und werden erzeugt, indem konstante Argumente den rgb-, rgba-, hsl- und hsla-Funktionen zugewiesen werden.

hsla(0, 0, 0.5, 0.5) ; transparent rot

Variablen

Formeln setzen sich aus einer Vielzahl von Berechnungen zusammen, deren Ergebnisse mit Hilfe von Variablen im Speicher bewahrt werden. Bezeichner, die mit einem Buchstaben beginnen müssen, verweisen auf Variablen.

x
MeineVariable
var_32

Bezeichner können sowohl in Klein- als auch in Großbuchstaben geschrieben werden, es dürfen jedoch keine Schlüsselworte verwendet werden (siehe letzte Überschrift). Diese sind ausschließlich für den Compiler reserviert und erzeugen bei Verwenden eine Fehlermeldung.

Fehlermeldung des Compilers bei Verwenden eines Schlüsselwortes

Bevor Variablen gelesen werden können, muss in sie hineingeschrieben werden. Dazu wird der Zuweisungsoperator=‘ verwendet.

z = @start

Eine Variable wird angelegt, wenn zum ersten Mal in sie hineingeschrieben wird. Die Voreinstellung für den Variablentyp ist ‘complex‘. Durch Vorstellen eines Typenschlüsselwortes (bool, int, float oder complex) kann der Variablentyp geändert werden. Es ist auch möglich, eine Variable vor dem Benutzen zu deklarieren.

int i
i = 10
complex y = (0,0)

Zuweisungen können ihrerseits wieder Ausdrücke sein, deren Resultate wiederum zugewiesen werden können. Den Werten x und y wird der Wert 1 zugewiesen.

x = y = 1

Um Variablen auszulesen, wird der Bezeichner des Ausruckes verwendet. Der Wert x wird ausgelesen und im Wert y gespeichert.

y = x

Auch folgende Berechnung ist möglich:

y = x * 3 + 7

Parameter

Damit ihr die Formeln anpassen könnt, ohne sie neu zu schreiben, werden Parameter benutzt. Diese werden wie Variablen benutzt, allerdings mit zwei Ausnahmen: es kann nicht in sie hineingeschrieben werden und es muss das @ vorangestellt werden, damit der Compiler sie als Parameter erkennt.

MeinMandelbrot(XAXIS_NOPARM) {
init:
  z = @start
loop:
  z = z^@power + #pixel
bailout:
  |z| <= @bailout

Die hier benutzten Parameter @start, @power und @bailout erscheinen nun im Karteireiter Formel (Formula), in denen ihr entsprechende Werte eintragen und somit das Aussehen des Fraktals beeinflussen könnt.

Der Parametertyp ist auf ‘complex‘ voreingestellt, der jedoch im param-Block des default-Abschnitts der Formel verändert werden kann. In diesen param-Blöcken könnt ihr weitere Parameteraufzählungen spezifizieren.

default:
  title = "MeinMandelbrot"
  center = (-0.5, 0)
  helpfile = "Uf*.chm"
  helptopic = "Html\formulas\standard\mandelbrot.html"

  param start
    caption = "Starting point"
    default = (0,0)
    hint = "The starting point parameter..."
  endparam
  param power
    caption = "Power"
    default = (2,0)
    hint = "This parameter sets the exponent..."
  endparam
  float param bailout
    caption = "Bailout value"
    default = 4.0
    min = 1.0
    hint = "This parameter defines..."
  endparam
}

Auf diese Weise könnt ihr Parameter für Benutzerfunktionen erstellen, die wie normale Funktionen angewendet werden können, jedoch mit der Ausnahme, dass ihr die aktuelle Funktion aus einer Liste weiterer Funktionen auswählen könnt.

Hier eine Beispiel mit geändertem loop-Block:

loop:
  z = z^@power + @myfunc(z) + #pixel

Der hier verwendete Parameter @myfunc sorgt im Karteireiter Formel (Formula) dafür, dass nun eine Drop-Down-Liste mit einer Reihe weiterer Funktionen (sin, sqr, ident etc.) erscheint.

Die Parameteraufzählungen werden im func-Block des default-Abschnitts genauer spezifiziert.

  heading
    caption = "Change Function"
  endheading

  func myfunc
    caption = "first"
    default = atanh()
    hint = "Select a function to change the formula."
  endfunc

Zum Abschluss seien noch einige Hinweise aus der Hilfedatei von Ultra Fractal zitiert:

  • Es ist zwar möglich in Parameter hineinzuschreiben, aber dies wird nicht empfohlen und kann Ihre Formel langsamer machen. Diese Möglichkeit wird nur aus Kompatibilitätsgründen zu alten Fractint-Formeln bereitgestellt.
  • Es gibt sechs vordefinierte Parameter: p1…p6, dazu vier vordefinierte Funktionen: fn1…fn4. Sie brauchen bei diesen Parametern und Benutzerfunktionen das Präfix ‘@’ nicht benutzen. Es wird dennoch empfohlen, dass Sie stattdessen ihre eigenen Namen benutzen (mit dem ‘@’-Präfix), damit die Formeln besser zu verstehen sind.
  • Die ‘param’ und ‘func’-Blöcke stellen zusätzlich Einstellungen zum Ändern der Werte für Benutzerfunktionen und der Parameter ‘caption’, ‘minimum’ und ‘maximum’ bereit. Zusätzlich können kleine Hilfe-Texte angegeben werden.
  • Parameter werden im User-Interface solange alphabetisch sortiert, bis Sie einen ‘param’-Block für jeden Parameter angeben. In diesem Fall erscheinen die Parameter in der Reihenfolge der ‘param’-Blocks in der Formel-Datei.
  • Im User-Interface können Sie durch zusätzliche Überschriften ebenfalls Parameter gruppieren.

Arrays

Ein Array ist eine Datenstruktur, die vom Programmierer selbst definiert wird und in der er mehrere Werte eines Datentyps ablegen kann. Bevor ihr das Array nutzen könnt, müsst ihr es deklarieren.

Der in eckigen Klammern stehende Index muss ein Integerwert zwischen 0 und der Anzahl der Elemente minus 1 sein.

int MeinArray[23]

Die Elemente in diesem Array könnt ihr wie normale Variablen nutzen. Es können auch mehrdimensionale Arrays deklariert warden, indem mehrere Werte innerhalb der eckigen Klammern stehen. Der Wert jeder Dimension muss ein konstanter Integerausdruck sein.

Parameter und die meisten vordefinierten Symbole sind als Konstaten verwendbar.

Farbe[255, 255, 255]
bool flags[#width, #height]
a[5, 10 – 2] = 1.5

Zu beachten ist, dass das Array nicht bei der Deklaration initialisiert wird. Stattdessen müssen die Elemente des Arrays explizit initialisiert werden, bevor auf sie zugegriffen wird.

Bedingungen

Gewisse Programmteile sollen nur ausgeführt werden, wenn bestimmte Kriterien erfüllt sind. Basis dieser Entscheidungen sind die sogenannten if-Statements.

Die Syntax lautet wie folgt:

if <Boole'scher Ausdruck>
  <Statements>
[elseif <Boole'scher Ausdruck>
  <Statements>]
[else
  <Statements>]
endif

Der Teil zwischen den eckigen Klammern ist optional.

Bool’sche Ausdrücke können mittels Vergleichsoperatoren verglichen bzw. durch logische Operatoren miteinander verknüpft werden. (siehe Operatoren)

if 8 > 4 && 8 > 2
  x = 8
elseif 4 > 2
  x = 4
else
  x = 2
endif

Die obige Konstruktion kann so gelesen werden: “Wenn die Bedingung 8 größer 4 UND 8 größer 2 erfüllt ist, dann (und nur dann) ist x gleich 8. Sonst wenn die Bedingung 4 größer 2 erfüllt ist, dann (und nur dann) ist x gleich 4, sonst ist x gleich 2.

Ihr könnt auch if-Statements so oft ineinander verschachteln wir ihr wollt, nur verliert dabei nicht irgendwann den Überblick.

Schleifen

In Ultra Fractal existieren zwei Konstrukte für Schleifen, zum Einen die while-Schleife und zum Anderen die repeat-Schleife.

Die Syntax beider Schleifen lautet wie folgt:

while <Boole'scher Ausdruck>
  <Statements>
endwhile

repeat
  <Statements>
until <Boole'scher Ausdruck>

Im Gegensatz zum if-Statement, das die Anweisung bei erfüllter Bedingung nur einmal ausgeführt und anschließend das Programm fortsetzt, wird bei der while-Schleife nach Ausführen der Anweisung wieder zurück zur Schleifenbedingung gesprungen und so lange wiederholt, wie der Bool’sche Ausdruck wahr ist. Ist der Bool’sche Ausdruck dagegen falsch, werden die Anweisungen gar nicht erst ausgeführt.

Die repeat-Schleife wiederholt die Anweisung so lange, bis der Bool’sche Ausdruck wahr wird.

Es ist wichtig dafür zu sorgen, dass Schleifen nicht nur bei Bedarf aufgerufen, sondern vor allem auch wieder abgebrochen werden.

Nachfolgend zwei Beispiele zum Ermitteln der Fakultät von 23.

int n = 23
float x = 1
while (n > 1)
  x = x * n
  n = n - 1
endwhile

int n = 23
float x = 1
if n > 1
  repeat
    x = x * n
    n = n - 1
  until n == 1
endif

Auch Schleifen könnt ihr beliebig oft verschachteln.

Schlüsselworte

Schlüsselworte sind Wörter, die ausschließlich dem Compiler vorbehalten sind und dürfen in Parametern sowie Variablen nicht verwendet werden. Hier die Liste der Schlüsselworte:

  • bool
  • color
  • complex
  • else
  • elseif
  • endfunc
  • endheading
  • endif
  • endparam
  • endwhile
  • false
  • float
  • func
  • heading
  • if
  • int
  • param
  • repeat
  • true
  • until
  • while

Ich wünsche Euch viel Spaß!

Formeln schreiben – Teil 1 by Oliver Konow is licensed under CC BY-NC-ND 4.0