Geometrisches Programmieren leicht gemacht

Seite 3: Precompiler

Inhaltsverzeichnis

Bei der Umsetzung mathematischer Probleme in Code können Programmierer aus verschiedenen Arbeitsbereichen von den Vorzügen der Geometrischen Algebra profitieren. In puncto Verwertbarkeit ist die Implementierung in einer Sprache wie C++ oder Java wünschenswert. Mit regulärer Syntax lassen sich derartige Rechnungen hier jedoch nur mit vergleichsweise hohem Aufwand realisieren. Die Möglichkeit, CLUScript-Code in moderne Sprachen einzubetten, könnte sich also als hilfreich erweisen. Der sogenannte Gaalop Precompiler versucht, diesem Ziel gerecht zu werden, indem er eingebetteten Code der Geometrischen Algebra durch Präkompilierung automatisch in C/C++/OpenCL/CUDA-Sourcecode übersetzt [2].

Das folgende Beispiel zeigt anhand einer C++-Funktion, wie sich CLUScript-Code in Programmen nutzen lässt. Da im vorliegenden Fall nur der Austausch von Skalaren zwischen eingebettetem CLUScript und umschließendem C++-Code nötig ist, findet die sogenannte Interface-Funktion mv_get_bladecoeff() Verwendung, um die Länge der Kette aus dem Multivektor kette abzufragen. Der gefundene Wert dient als Rückgabewert der gelisteten C++-Funktion computeChainLength(), deren Aufgabe es ist, die Länge der Kette mit allen als C++-Variablen definierten Eingabewerten r1, r2, r3, ... zu berechnen.

float computeChainLength(float r1, float r2, float r3,
float x1, float y1,
float x2, float y2,
float x3, float y3,
float zh, float aufh) {
#pragma gpc begin
#pragma clucalc begin

//Erstelle drei Kugeln, deren Mittelpunkte die
//Aufstellpunkte der Stangen und die
//Radien die Stangenlängen sind.

P1 = VecN3(x1,y1,0);
S1 = P1 - 0.5*r1*r1*einf;
P2 = VecN3(x2,y2,0);
S2 = P2 - 0.5*r2*r2*einf;
P3 = VecN3(x3,y3,0);
S3 = P3 - 0.5*r3*r3*einf;


//Die drei Stangen aneinanderlehnen (Kugelschnitt)

?Pp = S1^S2^S3;

//Bestimme den Aufhängungspunkt durch die
//Extraktion des ersten Punktes aus dem entstandenen Punktpaar

P4 = ExtractFirstPoint(Pp);

//Definiere die xy-Ebene (hat den Normalenvektor e3 in z-Richtung)

PlaneXY = e3;

//Berechne den Abstand zwischen xy-Ebene und Aufhängungspunkt

hoehe = PlaneXY.P4;

//Berechne die Kettenhöhe.

?kette = hoehe-zh-aufh;
#pragma clucalc end


return mv_get_bladecoeff(kette,1);
#pragma gpc end
}

Dem aufmerksamen Leser werden die mit #pragma gekennzeichneten Präprozessor-Kommandos auffallen: Das clucalc-Pragma deutet "puren" CLUScript-Code an, während das gpc-Pragma um es herum auftretende Import- oder Export-Interface-Funktionen zum Umwandeln von C++-Variablen/Arrays von/nach Multivektoren enthalten kann. Die Pragmas sind, wie auch der von ihnen eingeschlossene CLUScript-Code, nicht C++-konform und sind vor dem eigentlichen Kompilieren zu entfernen. Dem nimmt sich eine von CMake angestoßene Vorkompilierung an, die mit den meisten modernen Toolchains (Visual Studio, GNU Make etc.) kompatibel ist.

Der präkompilierte Code hat folgende (gekürzte) Form:

#line 1 "C:/test/Test9_CXX_ComputeChainLength.cpg"
#include <math.h>


float computeChainLength(float r1, float r2, float r3,
float x1, float y1,
float x2, float y2,
float x3, float y3,
float zh, float aufh) {
#line 10 "C:/test/Test9_CXX_ComputeChainLength.cpg"
//#pragma gpc multivector kette
float kette;
//#pragma gpc multivector Pp
float Pp[4];


//#pragma gpc multivector_component Pp e1^e2^einf Pp[0]
Pp[0] = (x1 / 2.0 * y2 - x2 / 2.0 * y1) * y3 * y3 +
(((-(x1 / 2.0 * y2 * y2)) + x2 / 2.0 * y1 * y1) - x1 / 2.0 * x2 * x2 +
(x1 * x1 / 2.0 - r1 * r1 / 2.0) * x2 + r2 * r2 / 2.0 * x1) * y3 +
x3 / 2.0 * y1 * y2 * y2 + (((-(x3 / 2.0 * y1 * y1)) +
x1 / 2.0 * x3 * x3 + (r1 * r1 / 2.0 - x1 * x1 / 2.0) * x3) -
r3 * r3 / 2.0 * x1) * y2 + ((-(x2 / 2.0 * x3 * x3)) +
(x2 * x2 / 2.0 - r2 * r2 / 2.0) * x3 + r3 * r3 / 2.0 * x2) * y1;
//#pragma gpc multivector_component Pp e1^e2^e0 Pp[1]
Pp[1] = (x2 - x1) * y3 + (x1 - x3) * y2 + (x3 - x2) * y1;
//#pragma gpc multivector_component Pp e1^einf^e0 Pp[2]
Pp[2] = (x2 / 2.0 - x1 / 2.0) * y3 * y3 + ... // gekürzt
//#pragma gpc multivector_component Pp e2^einf^e0 Pp[3]
Pp[3] = (y2 / 2.0 - y1 / 2.0) * y3 * y3 + ... // gekürzt
//#pragma gpc multivector_component kette 1 kette
kette = (((-(Pp[1] * zh)) + sqrtf(fabs(Pp[3] * Pp[3] + Pp[2] *
Pp[2] + 2.0 * Pp[0] * Pp[1]))) - aufh * Pp[1]) / Pp[1];


#line 31 "C:/test/Test9_CXX_ComputeChainLength.cpg"


return kette;
#line 34 "C:/test/Test9_CXX_ComputeChainLength.cpg"
}

Wie man erkennen kann, ist der Code nach dem Ersetzen aller #pragma- und CLUScript-Ausdrücke nun konform zum C++-Standard.