(2007-05-20)

Tools :: jdigrain :: Vorstellung

Vorstellung

Im Winter 2006/2007 lief in Magdeburg auf dem Blauen Bock das vom Magdeburger Club und der BlinkenArea initiierte Projekt bluebox. Im Rahmen eines Häuser-Contests der Studentencommunity WebUni habe ich zusammen mit Tux ein Java-Programm geschrieben, das für diese Installation Filme generiert, die den Regeneffekt aus dem Film Matrix nachbilden sollen. Dieses Programm, das ich jdigrain genannt habe, möchte auf dieser Seite vorstellen und zum Download anbieten.

Screenshot

Screenshot Simulator jdigrain

Wem das hier zu statisch ist, der kann sich das ganze auch im Simulator anschauen.

Implementierungsdetails des Programms

Ein- und Ausblenden

Im Laufe der Programmierung hatte ich die Erfahrung gemacht, dass der Film nicht schön wirkt, wenn die Tropfen gleich mit voller Intensität loslegen. So ein sanftes Ein- und Ausblenden wäre klasse und da mir ein schnödes lineares Einblenden zu langweilig war, sollte es gleich was rundes sein. Letztendlich ist das ziemlicher Käse, weil man die Helligkeitsunterschiede sowieso nicht sieht, aber sei's drum.

bluebox start intensity

Dieses Polynom benutze ich zum Ein- und Ausblenden, eingeblendet wird bis zum Zeitpunkt 6 und ausgeblendet ab dort. Die Skalierung ist relativ willkürlich und kann von dem Maximum hier (12) leicht ebenso leicht auf die 127 von bluebox skaliert werden wie die 6 auf beliebige Zeiten, denn ich habe das ganze nicht als Wertetabelle irgendwo abgelegt, sondern berechne direkt das dahinter stehende Polynom, dessen Koeffizienten ich mir zuvor mit Matlab zusammengebaut habe. Die Koeffizienten werden aus einer .csv-Datei in eine Liste gelesen. Die Auswertung erfolgt in einer kleinen Schleife:

private static double polyVal(List<Double> coeffDouble, double x) {
    int n = coeffDouble.size();    // das ist polynomgrad +1
    // horner schema wie in matlab's polyval
    double y = 0;
    if (!coeffDouble.isEmpty()) {
        y = coeffDouble.get(0);
    }
    for (int i=1; i<n; i++) {
        y = x * y + coeffDouble.get(i);
    }
    return y;
}

Allgemeine Klassen

Diesen Algorithmus benutze ich dann um Listen mit den Fade-In- und Fade-Out- Helligkeiten über die Zeit zu generieren, die ich dem Konstruktor für mein Animationsobjekt übergebe. Das bekommt außerdem noch Breite, Höhe und seine eigene Länge mitgegeben und ist eigentlich eine Liste aus Frame-Objekten, die dann in einer Schleife ausgelesen und zu einem Objekt der Klasse Movie hinzugefügt werden.

Die Klassen Frame und Movie sind ebenso wie z.B. Pixel oder BmlStorage von Stefan Haun geschrieben worden und ich benutze die einfach. Danke nochmal an Stefan an dieser Stelle, auch dafür, dass ich die Klassen im Paket zusammen mit dem anderen Kram hier zum Runterladen anbieten darf.

Kern des Programms

Der Hauptteil des Programms passiert dann in der Methode compStep der Klasse Animation:

private void compStep() {
    currentFrame = new Frame(this.width, this.height);

    for (int j = 0; j < this.numSlots; j++) {
        // Geschwindigkeit der Slots anpassen

        // schauen ob ein neuer Tropfen reinpasst, wenn ja neuen Tropfen
        // reinwerfen, es sei denn, wir sind schon über die Animations-
        // dauer weg
        if ( (this.currentPosition < this.length) &&
                (this.slots[j].freeForDrop()) ) {
            // TODO exceptions abfangen?
            if (this.r1.nextDouble() < wuselFaktor) {
                this.slots[j].addDrop(this.startIntensity[currentPosition],
                        this.r2.nextInt(8) + 3);
                slots[j].setNextBlack(true); // deprecated?
            } else {
                slots[j].addDrop(Pixel.COL_DARK, this.r3.nextInt(4) + 1);
                slots[j].setNextBlack(false);
            }
        }
        // Slot seine Tropfen bewegen (und faden) lassen --> slot.stepAhead
        // das sollte auch gleichzeitig alle rausgelaufenen entfernen
        this.slots[j].stepAhead();
        // vom Slot die aktuelle Spalte Pixel ausgeben lassen und für
        // den aktuellen Frame merken bzw. setzen
        Pixel[] tmpCol = this.slots[j].getSlotPix();
        for (int k = 1; k < tmpCol.length; k++) {
            currentFrame.setPixel(j, k - 1, tmpCol[k]); 
            // tmpCol ist ja einen länger ;)
        }
        currentFrame.setDuration(frameDuration);
    }

    this.currentPosition++;
}

Slots und Tropfen

Man sieht im obigen Beispiel schön, dass eine Animation aus genau soviel Objekten der Klasse Slot besteht, wie der Bildschirm breit ist. In so einen Slot werden fortlaufend, sofern das oberste Feld frei wird, Objekte der Klasse Drop »reingeworfen«, wobei es keine Pausen zwischen zwei hellen Tropfen gibt, sondern stattdessen komplett dunkle Tropfen. Diese Tropfen werden dann vom jedem Slot selbst in der Methode stepAhead nach bestimmten Vorgaben (wie z.B. Geschwindigkeit) weiterbewegt, dabei sind alle Slots voneinander unabhängig. Die »Slotgeschwindigkeiten«, d.h. die Geschwindigkeit aller Tropfen in einem Slot, wird aus einem bestimmten Bereich zufällig im Konstruktur der Animation festgelegt. Die Tropfen bewegen sich so Stück für Stück durch die Slots und werden auch wieder entfernt, wenn sie den sichtbaren Bereich des Slots verlassen haben.

Die Tropfen wiederum sind Objekte der Klasse Drop. Sie haben eine bestimmte Länge und speichern diese ebenso selbst wie die Position ihres »Kopfes«. In einem Array sind die Helligkeiten der Pixel, die so ein Tropfen hat angegeben. Zur Berechnung aller Helligkeiten aller Pixel eines Frames, wird letztendlich über den Umweg der Slots (nur diese kennen ja »ihre« Tropfen) auf diese Werte zugegegriffen.

Download, Installation und Lizenz

Wie viele andere Teile der Projekte BlinkenLights und BlueBox auch, steht dieses Programm unter der GPL. Entsprechende Hinweise habe ich in alle Quelldateien gepackt. Die Lizenz liegt dem Archiv auch nochmal bei. Leider kann ich kein fertiges Out-of-the-Box-Paket anbieten. Ich habe einfach die entsprechenden Projekt-Ordner aus Eclipse kopiert (incl. Projekt-Dateien). Diese Ordner müssten sich so als Projektordner wieder einbinden lassen. Wichtig ist noch, dass in der Datei JDigRain.java in Zeile 127 der Pfad zur Ausgabedatei angepasst wird, dann sollte das Ding schon ein fertiges Video auswerfen können.

Zu guter Letzt noch der eigentliche Link: bluebox_jdigrain_jlib.zip