Startseite < Informatik < Algorithmen Datenstrukturen / Software-Engineering / Programmiersprachen < Compiler Interpreter < Setty Tinyray < Tinyray-Scanner Tinyray-Parser / Tinyray-Language < Tinyray-LanguageKit [ DRUCK , 2004 , 2005 , 2006 , 2007 , 2008 , 2009 ] Tinyray-Visitables Tinyray-Visitors > Tinyray-Raytracer / Tinyray-Wallpapers > > / Java C/C++ POV-Ray LaTeX > / Künstliche Intelligenz > Schach Privates / Inhalt >
Tinyray-Visitables
Ein Visitable-Knoten kann von einem Visitor besucht werden (Besuchermuster).
Einführung Die Knoten des LanguageKit
Die Rolle eines besuchbaren Knotens
Die Knoten des Syntaxbaumes, die der LanguageKit erzeugt, sind vom Interface Visitable abgeleitet. Der Vorteil, der dabei ensteht, ist, dass die Knoten kaum eigene Methoden mitbringen müssen; die Implementierung eines Knotens fällt deshalb sehr schlank aus (Übersichtlichkeit). Die einzig erforderliche Methode lautet:
  1. R accept(Visitor, A)
Mit dieser Methode akzeptiert der Knoten den Besuch eines Tinyray-Visitors (Interface), wobei R der Rückgabetyp ist und A der Typ eines zusätzlichen Arguments.

Mit den besuchbaren Knoten ist es nun möglich aspektorientiert zu programmieren. Je nach Bedarf kann ein neuer Visitor erstellt werden, ohne dass die Knoten damit belastet werden oder umgeschrieben werden müssen. Tinyray stellt ein paar unterschiedliche Besucher (Visitors) zur Verfügung:
  1. Tinyray-PrettyPrint
  2. Tinyray-DotCode
  3. Tinyray-PovRayCode
  4. Tinyray-GlutCCode
  5. Tinyray-VRMLCode
Unterschied zu Setty
Jeder einzelne Knoten des Setty-Syntaxbaumes implementiert folgende Methoden zur Übersetzung in andere Sprachen:
  1. public String generateCCode()
  2. public String generatePascalCode()
  3. public String generateSmlCode()
Bei drei Methoden, die im Prinzip nichts miteinander zu tun haben, wird die Implementierung eines Knotens schon recht unübersichtlich, was bei nur drei Methoden noch tolerierbar wäre. Möchte man aber nun, den Syntaxbaum in eine weitere Sprache (z. B. Java) übersetzen, so müsste man jedem Knoten eine weitere Methode (z. B. generateJavaCode) hinzufügen. Was zur Folge hätte, dass die Implementierung noch unübersichtlicher werden würde. Die Implementierung wäre über alle Knoten-Klassen verstreut!

Tinyray mit seinen besuchbaren Knoten würde hingegen nur einen neuen Visitor (z. B. JavaCode) hinzubekommen, diesen könnte man sogar als Plugin einem bereits installierten System hinzufügen. Das ist doch ein besonders wichtiger Vorteil des Visitor-Patterns:
  1. Ein Visitor kann nachträglich als PlugIn einem bereits installieren Software-System hinzugefügt werden — genial! 1







Vorteile des Visitor-Patterns sind:
  • Übersichtlichkeit
  • Erweiterbarkeit
  • Wartbarkeit
  • Kapselung von Aspekten
  • PlugIn-Fähigkeit
Implementierung Implementierung der Knoten des LanguageKit
Zunächst überzeugt das allgemeingehaltene und zugleich primitive Interface, welches einen beliebigen Knoten besuchbar macht, durch seine schlichte, aber wirkungsvolle Gestalt, die Schnittstelle Visitable.
Visitable.java
package tinyray.language;

/**
 * Das Interface Visitable kennzeichnet ein von einem Visitor
 * besuchbares Objekt (Design-Pattern: Visitor).
 */

public interface Visitable {

    /**
     * Der Besuch eines Visitors geschieht nur über diese Methode.
     * @param <R> Der generische Rückgabetyp.
     * @param <A> Der generische Argumenttyp.
     * @param visitor Der Besucher.
     * @param argument Das Argument.
     * @return Das Ergebnis des Besuches.
     */

    <R, A> R accept(Visitor<R, A> visitor, A argument);
}
Im Folgenden sehen Sie die dazugehörigen Implementierungen aller besuchbaren Knoten des Tinyray-Syntaxbaumes.

Achten Sie darauf, dass die accept-Methode den Aufruf stets an den übergebenen Visitor weiterleitet. So kann der übergebene Visitor mit dem entsprechenden besuchbaren Knoten aktiv werden. Was nun der Visitor mit dem Knoten anstellt, hängt vom jeweiligen Visitor ab. Man könnte sagen, dass der Knoten sich blindlings dem Visitor anvertraut.
Tinyray.java
package tinyray.language;

import java.util.LinkedList;

/**
 * Die Klasse Tinyray stellt das Tinyray-Programm, welches die
 * 3D-Szene beschreibt, dar.
 */

public class Tinyray implements Visitable {

    /** Die Kamera, praktisch das Auge des Betrachters. */
    public Camera camera;

    /** Der Hintergrund, praktisch die Leinwand. */
    public Background background;

    /** Das ambiente Umgebungslicht. */
    public Ambience ambience;

    /** Der Nebel für etwas realistischere Bilder. */
    public Fog fog;

    /** Die Lichtobjekte. */
    public LinkedList<Sun> suns;

    /** Die geometrischen Objekte der 3D-Szene. */
    public LinkedList<Visitable> visitables;

    /**
     * Initialisiert das Tinyray-Programm.
     */

    public Tinyray() {
        this.camera = new Camera();
        this.camera.up.= new Real(1.0);
        this.background = new Background();
        this.ambience = new Ambience();
        this.fog = new Fog();
        this.suns = new LinkedList<Sun>();
        this.visitables = new LinkedList<Visitable>();
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Camera.java
package tinyray.language;

/**
 * Die Klasse Camera stellt die Kamera, quasi das Auge des Betrachters, dar.
 */

public class Camera implements Visitable {

    /** Die Kameraposition. */
    public Vector location;

    /** Der Punkt, auf den die Kamera gerichtet ist. */
    public Vector lookAt;

    /** Die Richtung der Oberseite der Kamera. */
    public Vector up;

    /**
     * Initialisiert die Kamera.
     */

    public Camera() {
        this.location = new Vector(0.0, 0.0, 10.0);
        this.lookAt = new Vector(); // Nullvektor = Zentrum
        this.up = new Vector(0.0, 1.0, 0.0);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Background.java
package tinyray.language;

/**
 * Die Klasse Background stellt den Hintergrund, quasi die Leinwand,
 * der Szene dar.
 */

public class Background implements Visitable {

    /** Die Hintergrundfarbe. */
    public Vector color;

    /**
     * Initialisiert die Background-Instanz.
     */

    public Background() {
        this.color = new Vector(); // Nullvektor = Schwarz
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Ambience.java
package tinyray.language;

/**
 * Die Klasse Ambience stellt die globale, lichtunabhänige Farbe der
 * geometrischen Objekte einer Szene dar.
 */

public class Ambience implements Visitable {

    /** Die globale, ambiente Farbe. */
    public Vector color;

    /**
     * Initialisiert die Ambience-Instanz.
     */

    public Ambience() {
        this.color = new Vector(1.0, 1.0, 1.0);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Fog.java
package tinyray.language;

/**
 * Die Klasse Fog stellt den Nebel der Szene dar.
 */

public class Fog implements Visitable {

    /** Die Nebelfarbe. */
    public Vector color;

    /** Die Nebeldichte. */
    public Real density;

    /**
     * Initialisiert den Nebel mit der schwarzen Farbe ohne Dichte.
     */

    public Fog() {
        this.color = new Vector(); // Nullvektor = Schwarz.
        this.density = new Real(0.0);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Defaults.java
package tinyray.language;

/**
 * Die Klasse Defaults kapselt die globalen Parameter eines Tinyray-Programms.
 */

public class Defaults implements Visitable {

    /** Die globalen Parameter. */
    public Parameters parameters;

    /**
     * Initialisiert die globalen Parameter.
     */

    public Defaults() {
        this.parameters = new Parameters();
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Sun.java
package tinyray.language;

/**
 * Die Klasse Sun stellt die Quelle des Sonnenlichts einer Szene dar.
 */

public class Sun implements Visitable {

    /** Die Richtung, in der die Sonne liegt. */
    public Vector direction;

    /** Die Farbe der Sonne. */
    public Vector color;

    /**
     * Initialisiert die Sonne.
     */

    public Sun() {
        this.direction = new Vector();
        this.color = new Vector();
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Bounding.java
package tinyray.language;

import java.util.LinkedList;

/**
 * Die Klasse Bounding vereint beliebig viele geometrische Objekte der
 * Szene zu einem Cluster, um eventuell schnelleres Rendern erzielen zu
 * können. Diese Klasse könnte zum Beispiel als Bounding-Box oder auch
 * als Bounding-Sphere realisiert werden.
 */

public class Bounding implements Visitable {

    /** Beliebige Objekte der Szene (vorzugsweise Geo-Objekte). */
    public LinkedList<Visitable> visitables;

    /**
     * Initialisiert die Bounding-Instanz.
     */

    public Bounding() {
        this.visitables = new LinkedList<Visitable>();
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Geo.java
package tinyray.language;

/**
 * Die Klasse Geo ist eine abstrakte Basisklasse für alle
 * geometrischen Objekte in der Szene wie zum Beispiel
 * "Triangle" oder "Sphere".
 */

public abstract class Geo implements Visitable {

    /**
     * Die Parameter eines Geoobjekts wie zum Beispiel
     * die lichtabhängige Farbe eines Objekts.
     */

    public Parameters parameters;

    /**
     * Initialisiert das Geoobjekt mit den Parametern.
     */

    public Geo() {
        this.parameters = new Parameters();
    }
}
Triangle.java
package tinyray.language;

/**
 * Die Klasse Triangle stellt ein geometrisches Objekt, ein Dreieck,
 * der Szene dar.
 */

public class Triangle extends Geo {

    /** Ein EckPunkt des Dreiecks. */
    public Vector x;

    /** Ein EckPunkt des Dreiecks. */
    public Vector y;

    /** Ein EckPunkt des Dreiecks. */
    public Vector z;

    /**
     * Initialisiert das Dreieck.
     */

    public Triangle() {
        super();
        this.= new Vector();
        this.= new Vector(1.0, 0.0, 0.0);
        this.= new Vector(0.0, 1.0, 0.0);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Sphere.java
package tinyray.language;

/**
 * Die Klasse Sphere stellt ein geometrisches Objekt, eine Kugel,
 * der Szene dar.
 */

public class Sphere extends Geo {

    /** Das Zentrum der Kugel. */
    public Vector center;

    /** Der Radius der Kugel. */
    public Real radius;

    /**
     * Initialisiert die Sphere.
     */

    public Sphere() {
        super();
        this.center = new Vector(); // Nullvektor = Zentrum
        this.radius = new Real(1.0);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Plane.java
package tinyray.language;

/**
 * Die Klasse Plane stellt ein geometrisches Objekt dar.
 * Die Plane zählt zu den unendlichen Objekten und wird in der
 * Mathematik als Ebene verstanden.
 */

public class Plane extends Geo {

    /** Ein Punkt der Ebene. */
    public Vector x;

    /** Ein Punkt der Ebene. */
    public Vector y;

    /** Ein Punkt der Ebene. */
    public Vector z;

    /**
     * Initialisiert eine durch die Koordinaten-Achsen X und Y
     * aufgespannte Ebene.
     */

    public Plane() {
        super();
        this.= new Vector();
        this.= new Vector(1.0, 0.0, 0.0);
        this.= new Vector(0.0, 1.0, 0.0);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Parameters.java
package tinyray.language;

import java.util.LinkedList;

/**
 * Die Klasse Parameters stellt die Parameter eines geometrischen Objekts
 * einer Szene dar wie zum Beispiel die Farbe und die Lichtreflektionsstärke.
 */

public class Parameters implements Visitable {

    /** Die Parameter ein sollten besuchbare Vektoren und Real-Objekte sein. */
    public LinkedList<Visitable> visitables;

    /**
     * Initialisiert die Parameter.
     */

    public Parameters() {
        this.visitables = new LinkedList<Visitable>();
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Vector.java
package tinyray.language;


/**
 * Die Klasse Vector stellt einen dreidimensionalen Vektor mit den skalaren
 * Typ Real dar. Dieser Vektor kann einen Ortsvektor oder eine Farbvektor
 * repräsentieren.
 */

public class Vector implements Visitable {

    /** Die X-Komponente des Vektors. */
    public Real x;

    /** Die Y-Komponente des Vektors. */
    public Real y;

    /** Die Z-Komponente des Vektors. */
    public Real z;

    /**
     * Initialisiert den Vektor als Nullvektor.
     */

    public Vector() {
        this(0.0, 0.0, 0.0);
    }

    /**
     * Initialisiert den Vektor mit den gegebenen Komponenten.
     * @param x Die X-Komponente.
     * @param y Die Y-Komponente.
     * @param z Die Z-Komponente.
     */

    public Vector(double x, double y, double z) {
        this.= new Real(x);
        this.= new Real(y);
        this.= new Real(z);
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }

    /**
     * Addiert zwei gegebene Vektoren und gibt die Summe zurück.
     * @param u Erster Summand.
     * @param v Zweiter Summand.
     * @return Die Summe.
     */

    public static Vector add(Vector u, Vector v) {
        Vector result = new Vector();
        result.= new Real(u.x.value + v.x.value);
        result.= new Real(u.y.value + v.y.value);
        result.= new Real(u.z.value + v.z.value);
        return result;
    }

    /**
     * Subtrahiert zwei gegebene Vektoren und gibt die Differenz zurück.
     * @param u Der Minuend.
     * @param v Der Subtrahend.
     * @return Die Differenz.
     */

    public static Vector sub(Vector u, Vector v) {
        Vector result = new Vector();
        result.= new Real(u.x.value - v.x.value);
        result.= new Real(u.y.value - v.y.value);
        result.= new Real(u.z.value - v.z.value);
        return result;
    }

    /**
     * Generiert einen Antivektor zum gegebenen Vektor und gibt diesen
     * zurück.
     * @param v Der originale Vektor.
     * @return Der Antivektor.
     */

    public static Vector anti(Vector v) {
        return Vector.sub(new Vector(), v);
    }

    /**
     * Normalisiert den gegebenen Vektor auf die Länge 1.
     * @param v Der zu normalisierende Vektor.
     * @return Der normalisierte Vektor.
     */

    public static Vector norm(Vector v) {
        Vector result = new Vector(1.0, 0.0, 0.0);
        double length = Math.sqrt(Vector.dot(v, v));
        if (length != 0.0) {
            result = Vector.mult(1.0 / length, v);
        }
        return result;
    }

    /**
     * Multipliziert einen Skalarwert mit einem Vektor und gibt das
     * Ergebnis zurück.
     * @param scalar Der Skalarwert.
     * @param v Der Vektor.
     * @return Das Produkt.
     */

    public static Vector mult(double scalar, Vector v) {
        Vector result = new Vector();
        result.= new Real(scalar * v.x.value);
        result.= new Real(scalar * v.y.value);
        result.= new Real(scalar * v.z.value);
        return result;
    }

    /**
     * Ermittelt das Skalarprodukt zwischen zwei Vektoren und gibt es zurück.
     * @param u Ein Vektor.
     * @param v Ein Vektor.
     * @return Das Skalarprodukt.
     */

    public static double dot(Vector u, Vector v) {
        return u.x.value * v.x.value
            + u.y.value * v.y.value
            + u.z.value * v.z.value;
    }

    /**
     * Prüft, ob der gegebene Vektor extrem kurz ist.
     * @param v Ein Vektor.
     * @return true, fast 0.
     */

    public static boolean isTiny(Vector v) {
        return Vector.dot(v, v) < 0.000000001;
    }

    /**
     * Ermittelt das Kreuzprodukt zwischen zwei Vektoren und gibt es zurück.
     * Das Kreuzprodukt liefert den Vektor der Senkrecht zu gegebenen Vektoren
     * steht.
     * @param u Ein Vektor.
     * @param v Ein Vektor.
     * @return Das Kreuzprodukt.
     */

    public static Vector cross(Vector u, Vector v) {
        Vector result = new Vector();
        result.= new Real(u.y.value * v.z.value - u.z.value * v.y.value);
        result.= new Real(u.z.value * v.x.value - u.x.value * v.z.value);
        result.= new Real(u.x.value * v.y.value - u.y.value * v.x.value);
        return result;
    }

    /**
     * Ermittelt die auf die Länge 1 normierte Normale zur Fläche, die von
     * den drei Punkten a, b und c aufgespannt wird.
     * @param a Der Punkt a.
     * @param b Der Punkt b.
     * @param c Der Punkt c.
     * @return Der normalisierte Normalenvektor.
     */

    public static Vector normal0(Vector a, Vector b, Vector c) {
        Vector u = Vector.sub(a, b);
        Vector v = Vector.sub(a, c);
        Vector normal = Vector.cross(u, v);
        return Vector.norm(normal);
    }
}
Real.java
package tinyray.language;

/**
 * Die Klasse Real kapselt eine Fließkommazahl.
 */

public class Real implements Visitable {

    /** Die Fließkommazahl. */
    public double value;

    /**
     * Initialisiert die Real-Zahl.
     * @param value Die Fließkommazahl.
     */

    public Real(double value) {
        this.value = value;
    }

    /**
     * Ermöglicht den Besuch eines Visitors.
     */

    public <R, A> R accept(Visitor<R, A> visitor, A argument) {
        return visitor.visit(this, argument);
    }
}
Anmerkung
Beim Lesen dieser Klassen haben Sie sicherlich festgestellt, dass alle Attribute public-deklariert sind, was unumstritten ein Nachteil ist, denn der Aufrufer bekommt somit auch die Möglichkeit die besuchbaren Knoten in einen inkonsitenten Zustand zu versetzen.
Ich wollte Ihnen nur einige Setter- und Getter-Methoden ersparen.