next up previous contents
Nächste Seite: Anwendung Aufwärts: Konvertierung von CML in Vorherige Seite: Ereignisablauf   Inhalt

Implementierung

Ausgangspunkt der Konvertierung ist eine CML-Datei, die dem Format aus Quellcode 4.3 entspricht. Neben den Namen der CML- und der X3D-Datei werden noch zwei weitere Dateinamen beim Aufruf des Konverters angegeben. Der genaue Aufruf lautet folgendermaßen:
java cml2x3d CML-Datei X3D-Datei 
             3D-Konfigurationsdatei Element-Datei
Durch eine separate Konvertierung wird die Darstellungsart Backbone (Abbildung 8.4) erzeugt, bei der ein Aminosäurerest nur durch das $ \alpha$-Kohlenstoffatom dargestellt wird. Innerhalb einer Proteinkette werden dann die $ \alpha$-Kohlenstoffatome aufeinanderfolgender Aminosäuren miteinander verbunden, so daß besonders gut die Sekundär- bzw. Tertiärstruktur eines Proteins zu beobachten ist. Beim Aufruf dieser Konvertierung werden dieselben Parameter wie bei der normalen Konvertierung angegeben.

Instanz der Konfigurationsdatei
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Config SYSTEM 'file:./Config.dtd'>
<Config>
   <Colors>
      <Background color='&white;'/>
      <Singlebond color='&yellow;'/>
      <Doublebond color='&red;'/>
      <Atom atomId="H" color='&light_blue;'/>
      <Atom atomId="HE"/>
      <Atom atomId="LI"/>
      <Atom atomId="BE"/>
      <Atom atomId="B" color='&brown;'/>
      <Atom atomId="C" color='&black;'/>
      <Atom atomId="N" color='&dark_blue;'/>
      <Atom atomId="O" color='&red;'/>
             :
             :
      <Atom atomId="AT"/>
      <Atom atomId="RN"/>
   </Colors>
   <Parameters>
      <!-- Atomgroesse = kovalenter Radius / Faktor -->
      <Atomsize factor='1.5'/>
      <!-- konfiguriert die Groesse des Bindungszylinders -->
      <Bondsize radius='0.05'/>
   </Parameters>
</Config>

In der zuletzt angegebenen Datei werden wieder die Elementinformationen abgelegt. Der Aufbau der Datei wurde in Kapitel 6.2 beschrieben. In der 3D-Konfigurationsdatei sind verschiedene Parameter enthalten, die die endgültige Darstellung in der 3D-Szene bestimmen. Das Format der Datei ist ebenfalls XML-basiert. Im Quellcode 7.6 ist eine Beispielinstanz abgebildet. Die Daten werden mit Hilfe eines SAX-Parsers in eine eigene Hashtabelle gespeichert, wodurch die Daten während der Konvertierung genutzt werden können. Für jedes Atom kann hier ein eigener Farbwert definiert werden. Die Größe der Atome wird durch einen allgemeinen Faktor festgelegt. Die letztendliche Größe wird dann als Quotient des kovalenten Radius und des Größenfaktors berechnet. Da der kovalente Radius bei der Berechnung des Kugelradius mit einfließt, werden kleinere Atome wie Wasserstoff auch kleiner in der Szene dargestellt. Für den Größenfaktor ist ein Wert voreingestellt, mit dem eine Darstellung wie Ball and Stick (Abbildung 8.2) erreicht wird. Die Bindungen zwischen den Atomen sind also sichtbar. Wird die Darstellung der Atome vergrößert, so daß die Bindungen nicht mehr sichtbar sind, erhält man eine Darstellung wie Spacefill (Abbildung 8.3). Stellt man den Faktor so ein, daß die Atome nicht mehr sichtbar sind, wird eine Darstellung wie Wireframe (Abbildung 8.5) erzeugt. Durch Variation des Größenfaktors lassen sich also verschiedene Darstellungen des Moleküls erzielen. Die Darstellung der Bindungen wird durch drei Einstellungsmöglichkeiten festgelegt. Zum Einen läßt sich der Radius des Bindungszylinders angeben und zum Anderen kann je ein Farbwert für die Einfach- bzw. Doppelbindungen definiert werden. Der letztendliche Farbwert der einzelnen Bindung wird aus diesen beiden Werten ermittelt. Die Grundlage der Berechnung bildet die Länge einer Bindung. Da die theoretischen Längen einer Einfach- bzw. Doppelbindung bekannt sind, läßt sich der Charakter der betrachteten Bindung bestimmen. Liegt z.B. die Länge der Bindung genau zwischen den Werten der Einfach- bzw. Doppelbindung, setzt sich sich der Farbwert jeweils zur Hälfte aus den vordefinierten Farbwerten zusammen.
Da die Wertigkeit einer Bindung nicht direkt in der CML-Datei abgelegt ist und nicht exakt bestimmt werden kann, kann so in der Darstellung darauf hingewiesen werden, welchen Charakter eine Bindung hat. Genauere Informationen können dann über das Hilfe-Fenster abgerufen werden. Die eigentliche Konvertierung wird wieder mit einem SAX-Parser realisiert. Dazu sind lediglich einige Methoden des ContentHandlers, die im Quellcode 3.3 zu sehen sind, zu implementieren.

void startDocument()
In dieser Methode wird die X3D-Datei initialisiert. Neben der Dokumenttypdeklaration werden auch die vorangestellten Deklarationen durchgeführt. Dazu gehören auch die Definitionen der Script-Knoten ortung, pse und weg, die die Kommunikation unter den 3D-Objekten regeln. Wichtiger sind jedoch die Deklarationen der beiden Prototypen für die Darstellung der Atome und Bindungen.
void startElement(String, String, String, Attributes)
Mit Hilfe dieser Methode wird festgelegt, wie der Inhalt eines Elementes verarbeitet wird. Wird z.B. das Start-Tag des atom- oder bond-Elementes eingelesen, wird eine Instanz des entsprechenden Java-Klasse erzeugt. Darin werden dann alle weiteren Informationen abgelegt. Wird hingegen ein Start-Tag eines Elementes eingelesen, das nur Text beinhaltet, wird lediglich ein Status definiert. Für jedes Element wird dabei ein eigener Status festgelegt, mit dem in der späteren Methode endElement() der Inhalt dem entsprechenden Element genau zugeschrieben werden kann.
void characters(char[], int, int)
Mit dieser Methode werden die Textfelder eines Elementes verarbeitet. Da in der Regel eine CML-Datei zu groß ist, um sie komplett zu bearbeiten, wird sie in mehrere Abschnitte unterteilt. Die Schnittstellen zwischen den einzelnen Teilstücken liegen in den Textfeldern der Elemente, so daß ein Textfeld über zwei Abschnitte verteilt sein kann. D.h. bei einem Textfeld kann die Methode zweimal aufgerufen werden. Der mit der Methode new String(char[], int, int) erzeugte String wird deshalb nur an die content-Variable, die den Elementinhalt beinhaltet, angehängt. Die Methode ist im Quellcode 7.7 zu sehen.

Die Methode characters()
public void characters(char[] ch, int start, int length) 
   throws SAXException {
   String s = new String(ch, start, length);
   if (s.trim().length() == 0)    return;
   content = content.concat(s);
}

void endElement(String, String, String)
Der zuvor eingelesene Inhalt wird am Ende des entsprechenden Elements verarbeitet. Wird ein End-Tag eines Elements eingelesen, daß ein Textfeld enthält, werden die Daten entsprechend abgespeichert. Bei einem Element wie:
<float builtin="z3" units="A">0.9763</float>
werden die Daten direkt in das zuvor angelegte Atom-Objekt als Z-Koordinate gespeichert. Anders verhält es sich bei den Elementen atom und bond. Hier wird jeweils eine X3D-Instanz des jeweiligen Prototypen erzeugt. Bei einer Atom-Instanz werden die Daten aus dem Java-Objekt und den Konfigurationsdateien genutzt, um die Instanz zu initialisieren. Zusätzlich werden noch die einzelnen Koordinaten der Atome aufaddiert und die Anzahl der Atome festgehalten. Darüber hinaus wird das Atom mit der größten Z-Koordinate ermittelt. Diese Informationen werden bei der späteren Berechnung des ViewPoint benötigt. Im Gegensatz zu einer Atom-Instanz müssen bei einer Bond-Instanz zwei Informationen berechnet werden.
  1. die exakte Position des Zylinders
  2. und die entsprechende Ausrichtung
Die Berechnung dieser Daten ist im Quellcode 7.8 zu sehen. Als erstes wird die Position des Zylinders bestimmt, die als Mittelpunkt aus den beiden Atompositionen bestimmt wird. In der Variable position wird die genaue Position abgelegt. Im nächsten Schritt wird die Ausrichtung des Zylinders berechnet. Der Zylinder, der die Bindung beschreibt hat die gleiche Ausrichtung, wie der durch die Koordinaten middle beschriebende Vektor. Ein Zylinder ist in der Grundstellung entlang der Y-Achse ausgerichtet. Man muß also zunächst den Winkel $ \alpha$ zwischen middle und Y-Achse mit der folgender Formel bestimmen:

cos$\displaystyle \alpha$ = $\displaystyle {\frac{{\vec{middle} \ast \vec{e_y}}}{{\vert\vec{middle}\vert \cdot \vert\vec{e_y}\vert}}}$        0 $\displaystyle \leq$ $\displaystyle \alpha$ $\displaystyle \leq$ $\displaystyle \pi$

mit    $\displaystyle \vec{{e_y}} $ = $\displaystyle \left(\vphantom{\begin{array}{c}0  1  0 \end{array}}\right.$$\displaystyle \begin{array}{c}0  1  0 \end{array}$$\displaystyle \left.\vphantom{\begin{array}{c}0  1  0 \end{array}}\right)$    und    |$\displaystyle \vec{{e_y}} $| = 1     $\displaystyle \Rightarrow$     cos$\displaystyle \alpha$ = $\displaystyle {\frac{{middleY}}{{\vert\vec{middle}\vert}}}$    0 $\displaystyle \leq$ $\displaystyle \alpha$ $\displaystyle \leq$ $\displaystyle \pi$

Danach muß die Rotationsachse bestimmt werden, um die der Zylinder gedreht werden soll. Diese ist orthogonal zu dem Vektor middle und zur Y-Achse. Ist die X-Koordinate von middle gleich Null, liegt middle in der Y/Z-Ebene und als Rotationsachse kann die X-Achse festlegt werden (Abbildung 7.3(a)).
Da eine Drehung gegen den Uhrzeigersinn erfolgt und bei der Berechnung der Winkel nur Werte zwischen 0 und $ \pi$ ermittelt werden, muß der Winkel gegebenenfalls zu 2$ \pi$ - $ \alpha$ korrigiert werden. Liegt hingegen middle nicht in Y/Z-Ebene (Abbildung 7.3(b)), muß die Rotationsachse berechnet werden. Da das Skalarprodukt des Vektors $ \vec{{n}} $, der die Rotationsachse beschreibt, mit dem Vektor middle und der Y-Achse gleich Null ist, muß $ \vec{{n}} $ die folgenden zwei Gleichungen erfüllen:

middleX . n1 + middleY . n2 + middleZ . n3 = 0
    n2     = 0

Da das Gleichungssyzstem unterbestimmt ist und middle und $ \vec{{e_y}} $ linear unabhängig sind, ist eine Variable im Gleichungssystem frei wählbar. Setzt man für n3 = 1, ergibt sich für n1 der Wert - middleZ/middleX. Somit sind die kompletten Koordinaten der Rotationsachse bestimmt. In Abhängigkeit von middleX muß gegebenenfalls wieder der Winkel zu 2$ \pi$ - $ \alpha$ korrigiert werden.

Abbildung 7.3: Lagemöglichkeiten der Rotationsachse
[Rotationsachse liegt auf der X-Achse] \resizebox*{0.45\textwidth}{!} {\includegraphics{pics/koora}} [Rotationsachse liegt nicht auf der X-Achse] \resizebox*{0.45\textwidth}{!} {\includegraphics{pics/koorb}}


Berechnung der Attribute des Bindungszylinders
// Koordinaten des ersten Atoms
double tempX_1 = bond.getFirst().getX();
double tempY_1 = bond.getFirst().getY();
double tempZ_1 = bond.getFirst().getZ();

// Koordinaten des zweiten Atoms
double tempX_2 = bond.getSecond().getX();
double tempY_2 = bond.getSecond().getY();
double tempZ_2 = bond.getSecond().getZ();

// halbierter Vektor zwischen den Atomen
double middleX = (tempX_2 - tempX_1) / 2;
double middleY = (tempY_2 - tempY_1) / 2;
double middleZ = (tempZ_2 - tempZ_1) / 2;

// Mittelpunkt zwischen den beiden Punkten
String position = new String((tempX_1 + middleX) + " "
    + (tempY_1 + middleY) + " " + (tempZ_1 + middleZ));

// Winkel zwischen middle und Y-Achse
double help = middleX * middleX + middleY * middleY
    + middleZ * middleZ;
double angle = Math.acos(middleY / Math.sqrt(help));
	    
// Bestimmung der Rotationsachse
String rotation;
if (middleX == 0) {
    rotation = "1 0 0 ";
    // falls notwendig "richtig" herum drehen
    if (middleZ < 0)
        angle = (2 * Math.PI) - angle;
}
else {
    rotation = -middleZ / middleX + " 0 1 ";
    // falls notwendig "richtig" herum drehen
    if (middleX > 0)
        angle = (2 * Math.PI) - angle;
}
// Angabe einer Rotation in X3D:
// 3 Koordinaten der Rotationsachse + Winkel
rotation = rotation + angle;

void endDocument()
Mit dieser Methode wird die X3D-Datei abgeschlossen. Um einen passenden Blickwinkel auf das Molekül zu erhalten, wird ein ViewPoint festgelegt. Das Molekül soll möglichst das Browserfenster füllen und dennoch komplett zu sehen sein.
Zunächst wird der Punkt festgelegt, wo der Betrachter beim Betreten der Szene steht. Realisiert wird das mit den zwei Punkten, die zuvor in der Methode endElement() festgelegt wurden. Zum Einen benötigt man den Mittelpunkt des Moleküs, der über das Array middlepoint berechnet werden kann. Im Array sind die Summen der einzelnen Koordinaten und die Anzahl der Atome abgelegt, so daß das arithmetische Mittel darüber gebildet werden kann. Zum Anderen werden die Koordinaten des Atoms mit der größten Z-Koordinate benötigt, die im Array zMax abgelegt sind. Anschließend wird die Projektion des Vektors zwischen beiden Punkten in die X/Z-Ebene berechnet. Die Koordinaten sind in den Variablen helpX und helpZ abgelegt. Der letztendliche Startpunkt setzt sich dann aus der Summe des Mittelpunktes und der zweifachen Länge der Projektion zusammen (Abbildung
7.4).

Abbildung 7.4: Skizze zur ViewPoint-Berechnung
\resizebox*{0.6\textwidth}{!}{\includegraphics{pics/koorc}}

Zusätzlich muß die Blickrichtung von diesem Punkt aus festgelegt werden. Dafür wird der Winkel zwischen der Projektion und der Z-Achse berechnet. Je nachdem ob die X-Koordinate des Mittelpunktes größer ist als die des zMax-Punktes, wird der Winkel korrigiert. Die Werte werden dann in den Feldern position und orientation des Knoten Viewpoint abgelegt.

Die Methode endDocument()
public void endDocument() throws SAXException {
    // Projektion von (zMax-middlepoint) in die XZ-Ebene
    double helpX = zMax[0] - (middlepoint[0] / middlepoint[3]);
    double helpZ = zMax[2] - (middlepoint[2] / middlepoint[3]);

    // Winkel zwischen help und Z-Achse
    double angle = Math.acos( helpZ /
        Math.sqrt(helpX * helpX + helpZ * helpZ));
    // Falls noetig Winkel korrigieren
    if (helpX < 0)
        angle = (2 * Math.PI) - angle;
	
    try {
        writer.write("Viewpoint {\n");
        writer.write("\tdescription \"start\"\n");
        writer.write("\torientation 0 1 0 " + angle + "\n");
        writer.write("\tposition " +
            ((middlepoint[0] / middlepoint[3]) + (2 * helpX))
            + " " + (middlepoint[1] / middlepoint[3]) + " " + 
            ((middlepoint[2] / middlepoint[3]) + (2 * helpZ))
            + "\n");
        writer.write("}\n");
        writer.close();
    }
    catch (IOException ioe) {
        System.err.println ("IOException" + ioe.getMessage());
        System.exit (1);
    }
}


next up previous contents
Nächste Seite: Anwendung Aufwärts: Konvertierung von CML in Vorherige Seite: Ereignisablauf   Inhalt
Oliver Krone 2003-04-28