StartseiteSitemapDownloadsHilfeImpressumPflichtenheft Chat


Startseite

Vorwort

Es war nur eine Frage der Zeit, wann ich endlich das berühmteste Puzzle der Welt (Rubik's Cube) in meine Homepage aufnehmen würde. Inspiriert durch die positive Resonanz auf das Vorgängerprojekt POV−Ray−Schachspiel, entwickelte ich ein POV−Ray−Framework für den supergenialen Zauberwürfel von Rubik.

Kleiner Vorgeschmack

Erste Zauberwürfelanimation des Frameworks


Motivation

Wenn Sie Lust haben zu lernen, wie man einen 3x3x3−Zauberwürfel löst oder programmiert, dann sehen Sie sich am besten auf den vorliegenden Seiten um. Zum Beispiel erwartet Sie auf POV−Ray−Zauberwürfel−Tests ein visuell aufbereitetes Tutorium für die Erlernung des Zauberwürfels.



Umgebung

Vernünftig scheint es mir, zunächst auf die Entwicklungsumgebung des Frameworks einzugehen, auch wenn dies ein wenig trocken erscheinen mag.

Entwicklungsumgebung

Als Erstes möchte ich hier die zentrale Entwicklungsdatei namens develop.pov anführen. In dieser Datei werden typische Szenenvorbereitungen (Konfiguration: Kamera, Hintergrund, Lichtquellen etc.) getroffen, um eine umfassende, visuelle Kontrolle während der Entwicklung eines Zauberwürfels stets zur Verfügung zu haben. Während der Entwicklung habe ich an dieser zentralen Datei permanent Korrekturen vorgenommen, so dass die Kamera zusammen mit den Lichtquellen quasi eine Einheit bildet.
Die Texturdeklarationen stellen häufig vorkommende Texturen zur Verfügung, die erst später von Bedeutung sind.

develop.pov

/* Kamera & Hintergrund */

camera { location <5.0, 4.4, -7.0> look_at <-0.8, -1.1, 0.0> } // Perspektive!
background { color rgb <0.5, 0.5, 0.5> } // grau

/* Lichtquellen */

light_source { <5.0, 3.5, -2.0> color rgb <0.7, 0.7, 0.7> } // ~ rechts oben
light_source { <-2.0, 5.8, -8.0> color rgb <0.7, 0.7, 0.7> } // ~ vorne oben
light_source { <-6.0, -7.0, 8.0> color rgb <0.7, 0.7, 0.7> } // ~ hinten unten

/* Texturen */

#default {
        texture {
                pigment { color rgb <0.2, 0.2, 0.2> } // ~ schwarz
                finish { ambient 0.88 phong 1.0 diffuse 0.9 } // ~ glänzend
        }
}

#declare TA = texture { pigment { color rgb <0.2, 0.2, 0.2> } } // Atom

#declare TXP = texture { pigment { color rgb <0.0, 0.0, 0.8> } } // XP = blau
#declare TXN = texture { pigment { color rgb <0.0, 0.7, 0.0> } } // XN = grün

#declare TYP = texture { pigment { color rgb <0.7, 0.7, 0.7> } } // YP = weiß
#declare TYN = texture { pigment { color rgb <0.7, 0.7, 0.0> } } // YN = gelb

#declare TZP = texture { pigment { color rgb <0.7, 0.4, 0.0> } } // ZP = orange
#declare TZN = texture { pigment { color rgb <0.7, 0.0, 0.0> } } // ZN = rot
Zur Entwicklungsumgebung gehören noch weitere Dateien wie develop.axes.pov (Koordinatenachsen) und develop.mirrors.pov (Rundumspiegel). Diese Dateien können wahlweise hinzu- bzw. weggenommen werden. Hierbei achtete ich besonders auf größt mögliche Unabhängigkeit.

develop.axes.pov

// Achsprototyp
#declare Axis = merge {
        cylinder { <-2.5, 0.0, 0.0>, <2.0, 0.0, 0.0>, 0.05 }
        cone { <2.0, 0.0, 0.0>, 0.12, <2.5, 0.0, 0.0>, 0.0 }
        texture {
                pigment {
                        checker
                        color rgbt <0.6, 0.6, 0.6, 0.6>,
                        color rgbt <0.3, 0.3, 0.3, 0.6>
                }
                finish { ambient 0.8 phong 0.0 }
                translate 0.5
        }
        no_shadow
}

// Drei Koordinatenachsen
object { Axis }
object { Axis rotate +90.0 * z }
object { Axis rotate -90.0 * y }

develop.mirrors.pov

// Lichtdurchlässiger Spezialspiegel zur Visualisierung der Würfelrückseiten
#declare Mirror = disc {
        <-3.2, 0.0, 0.0>, x, 2.8
        pigment { color rgb 0.4 }
        finish { reflection { 1.0 } ambient 0.1 diffuse 0.2 } // spiegelnd
        no_shadow // um der Lichtquelle nicht im Wege zu stehen
        no_reflection // damit nicht öfter als einmal gespiegelt wird
}

// Drei Spezialspiegel
object { Mirror translate <-0.2, 0.7, -1.5> } // links
object { Mirror rotate 90.0 * y translate <0.6, 0.5, 2.1> } // rechts
object { Mirror rotate 90.0 * z translate <0.4, 0.1, -0.9> } // unten
Später kommt noch eine weitere Umgebungsdatei, mit der man sich viel Schreibaufwand sparen kann, hinzu. Die Datei, von der die Rede ist, benötigt bereits Bausteine des Zauberwürfels (Atome). Aus diesem Grund verlasse ich die Umgebungspräsentation und gehe im Folgenden auf die elementare Konstruktion des Zauberwürfels ein.

Framework−Dateien

Das gesamte Framework besteht eigentlich aus nur drei Dateien:
  1. atoms.pov
  2. cube.pov
  3. patterns.pov
Dateien, so genannte Entwicklungsumgebungsdateien, die mit dem Präfix develop beginnen, sind für den späteren Gebrauch — also für Ihren eigenen Animationsfilm — nicht mehr von nöten. :−)



Bausteine

Würfelchen

Jeder, der schon einmal aus Ungeduld einen Zauberwürfel zerlegt hat, weiß, dass dieser aus vielen kleinen Würfelchen besteht. Ein 3x3x3−Zauberwürfel setzt sich aus 26 Würfelchen (8 Eckwürfelchen, 12 Kantenwürfelchen und 6 zentral liegende Würfelchen) zusammen. Diese Würfelchen bezeichne ich als Atome.
In folgender Framework−Datei atoms.pov finden Sie alle relevanten Deklarationen und Makros für die Generierung von konsistenten Atomen.

atoms.pov

/* Framework: POV-Ray-Zauberwürfel von Stefan K. Baur */

// ----------------------------------------------------------------------------

/* Standard-Atom */

// Atom = einzelner Baustein eines Zauberwürfels

// Generierung eines würfelartigen, im Zentrum liegenden Atoms (Normgröße = 1)
#macro CreateAtom(BorderWidth)
        superellipsoid { <BorderWidth, BorderWidth> scale 0.5 }
#end

#declare Atom = CreateAtom(0.13);

// ----------------------------------------------------------------------------

/* Tags */

// Tag = einzelner Farbaufkleber eines Atoms

// Generierung einfacher schattenloser Tags von bel. Texture und Ausrichtung
#macro CreateTag(Texture, Vector)
        #local Epsilon = 0.001;
        intersection {
                object { Atom scale 1 + Epsilon }
                object { Atom scale 1 - Epsilon inverse }
                object { Atom scale 0.8 translate 0.5 * Vector }
                texture { Texture }
                no_shadow
        }
#end

// ----------------------------------------------------------------------------

/* Atome */

// Generierung verschiedener Atomtypen

// Zentrales Atom einer Würfelfläche (eine Farbe bzw. ein Tag)
#macro CreateAtom1(Texture, Tag1)
        union {
                object { Atom texture { Texture } }
                object { Tag1 }
        }
#end

// Atom einer Würfelkante (zwei Farben bzw. zwei Tags)
#macro CreateAtom2(Texture, Tag1, Tag2)
        union {
                object { Atom texture { Texture } }
                object { Tag1 }
                object { Tag2 }
        }
#end

// Atom einer Würfelecke (drei Farben bzw. drei Tags)
#macro CreateAtom3(Texture, Tag1, Tag2, Tag3)
        union {
                object { Atom texture { Texture } }
                object { Tag1 }
                object { Tag2 }
                object { Tag3 }
        }
#end
Wie nun diese Framework-Datei mit der Entwicklungsumgebung zusammenspielt, möchte ich Ihnen nicht länger vorenthalten. :−)

develop.atoms.atom.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "atoms.pov"

/* Standardatom */

Atom
Das Standard-Atom im Koordinatensystem mit Rundumspiegel


An dieser Stelle möchte ich besonders auf die zusätzlichen Umgebungskomponenten in develop.axes.pov und in develop.mirrors.pov hinweisen, die das Standardatom umgeben. Wären diese Dateien nicht eingebunden (//#include ...), wäre nur das eine zentrale Atom auf grauem Hintergrund unverändert vorhanden.
Selbstverständlich benötigt man auch farbliche Aufkleber, die ich Tags genannt habe.

develop.atoms.tags.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "atoms.pov"

/* Tags */

CreateTag(TXP, +x) // rechts, blau
CreateTag(TXN, -x) // links, grün

CreateTag(TYP, +y) // oben, weiß
CreateTag(TYN, -y) // unten, gelb

CreateTag(TZP, +z) // hinten, orange
CreateTag(TZN, -z) // vorne, rot


// Für einen 1x1x1-Zauberwürfel bitte Folgendes aktivieren :-)

//object { Atom texture { TA } }
Farbaufkleber für das Standard-Atom


Nun komme ich zur anfangs erwähnten Umgebungsdatei, mit der man sich viel Schreibarbeit sparen kann. Diese Umgebungsdatei heißt develop.tags.pov und definiert die eben gerenderten Tags wie folgt:

develop.tags.pov

/* Tags */

#declare TagXP = CreateTag(TXP, +x); // rechts (blau)
#declare TagXN = CreateTag(TXN, -x); // links (grün)

#declare TagYP = CreateTag(TYP, +y); // oben (weiß)
#declare TagYN = CreateTag(TYN, -y); // unten (gelb)

#declare TagZP = CreateTag(TZP, +z); // hinten (orange)
#declare TagZN = CreateTag(TZN, -z); // vorne (rot)
Mit dieser Umgebungsdatei kann man sich nun endlich farbige Atome zusammenstellen.
Hier sehen Sie alle sechs mittleren, innerhalb einer Fläche liegenden Atome (atom1).

develop.atoms.atom1.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "atoms.pov"

#include "develop.tags.pov"

/* 6 mittige Atome */

// Atom rechts mittig (blau)
object { CreateAtom1(TA, TagXP) translate +x }

// Atom links mittig (grün)
object { CreateAtom1(TA, TagXN) translate -x }

// Atom oben mittig (weiß)
object { CreateAtom1(TA, TagYP) translate +y }

// Atom unten mittig (gelb)
object { CreateAtom1(TA, TagYN) translate -y }

// Atom hinten mittig (orange)
object { CreateAtom1(TA, TagZP) translate +z }

// Atom vorne mittig (rot)
object { CreateAtom1(TA, TagZN) translate -z }
6 Flächenatome


Alle zwölf Kantenatome (atom2) …

develop.atoms.atom2.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "atoms.pov"

#include "develop.tags.pov"

/* 12 Kantenatome */

// Atom unten rechts
object { CreateAtom2(TA, TagXP, TagYN) translate +x-y }

// Atom unten links
object { CreateAtom2(TA, TagXN, TagYN) translate -x-y }

// Atom unten hinten
object { CreateAtom2(TA, TagZP, TagYN) translate +z-y }

// Atom unten vorne
object { CreateAtom2(TA, TagZN, TagYN) translate -z-y }


// Atom oben rechts
object { CreateAtom2(TA, TagXP, TagYP) translate +x+y }

// Atom oben links
object { CreateAtom2(TA, TagXN, TagYP) translate -x+y }

// Atom oben hinten
object { CreateAtom2(TA, TagZP, TagYP) translate +z+y }

// Atom oben vorne
object { CreateAtom2(TA, TagZN, TagYP) translate -z+y }


// Atom rechts hinten
object { CreateAtom2(TA, TagXP, TagZP) translate +x+z }

// Atom rechts vorne
object { CreateAtom2(TA, TagXP, TagZN) translate +x-z }

// Atom links hinten
object { CreateAtom2(TA, TagXN, TagZP) translate -x+z }

// Atom links vorne
object { CreateAtom2(TA, TagXN, TagZN) translate -x-z }
12 Kantenatome


Und alle acht Eckatome (atom3) …

develop.atoms.atom3.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "atoms.pov"

#include "develop.tags.pov"

/* 8 Eckatome */

// Atom hinten rechts oben
object { CreateAtom3(TA, TagXP, TagYP, TagZP) translate +x+y+z }

// Atom hinten links oben
object { CreateAtom3(TA, TagXN, TagYP, TagZP) translate -x+y+z }

// Atom hinten rechts unten
object { CreateAtom3(TA, TagXP, TagYN, TagZP) translate +x-y+z }

// Atom hinten links unten
object { CreateAtom3(TA, TagXN, TagYN, TagZP) translate -x-y+z }


// Atom vorne rechts oben
object { CreateAtom3(TA, TagXP, TagYP, TagZN) translate +x+y-z }

// Atom vorne links oben
object { CreateAtom3(TA, TagXN, TagYP, TagZN) translate -x+y-z }

// Atom vorne rechts unten
object { CreateAtom3(TA, TagXP, TagYN, TagZN) translate +x-y-z }

// Atom vorne links unten
object { CreateAtom3(TA, TagXN, TagYN, TagZN) translate -x-y-z }
8 Eckatome


Abschließend möchte ich darauf hinweisen, dass die ovalen Flächen im Hintergrund Spiegel der Umgebungsdatei develop.mirrors.pov sind. Der Sinn dieser Rundumspiegel wird bei der Präsentation des Zauberwürfels sicher etwas klarer.



Zauberwürfel

Bei der Entwicklung des Zauberwürfels muss man nicht nur darauf achten, dass die Atome richtig liegen, sondern auch, dass die Rotationen der Ebenen adäquat ablaufen können. Die Tatsache, dass man einen Zauberwürfel auch in der POV−Ray−Welt verdrehen möchte, erschwert die ganze Codierung ein wenig. :−(
Eine vollständige POV-Ray-Realisierung des Zauberwürfels, die auch adäquate Drehungen zulässt, finden Sie in der vorliegenden Framework−Datei cube.pov. Nehmen Sie sich bitte für den folgenden Code ein wenig Zeit, da dieser das Herzstück des gesamten Frameworks ist.

cube.pov

/* Framework: POV-Ray-Zauberwürfel von Stefan K. Baur */

#include "atoms.pov"

// ----------------------------------------------------------------------------

/* Konfiguration */

// Konfigurationsmöglichkeit einer 3x3x3-Matrix (State), siehe "Cube_Init".
//   Jeder Slot dieser Matrix bekommt ein Grafikobjekt (Atome: A???).
#macro Cube_Config(State,

        A020, A120, A220,
        A021, A121, A221,
        A022, A122, A222,

        A010, A110, A210,
        A011, A111, A211,
        A012, A112, A212,

        A000, A100, A200,
        A001, A101, A201,
        A002, A102, A202)

        // Oberste Ebene
        #declare State[0][2][0] = object { A020 translate -x+y-z }
        #declare State[1][2][0] = object { A120 translate   +y-z }
        #declare State[2][2][0] = object { A220 translate +x+y-z }
        #declare State[0][2][1] = object { A021 translate -x+y   }
        #declare State[1][2][1] = object { A121 translate   +y   }
        #declare State[2][2][1] = object { A221 translate +x+y   }
        #declare State[0][2][2] = object { A022 translate -x+y+z }
        #declare State[1][2][2] = object { A122 translate   +y+z }
        #declare State[2][2][2] = object { A222 translate +x+y+z }

        // Mittlere Ebene
        #declare State[0][1][0] = object { A010 translate -x  -z }
        #declare State[1][1][0] = object { A110 translate     -z }
        #declare State[2][1][0] = object { A210 translate +x  -z }
        #declare State[0][1][1] = object { A011 translate -x     }
        #declare State[1][1][1] = object { A111 }
        #declare State[2][1][1] = object { A211 translate +x     }
        #declare State[0][1][2] = object { A012 translate -x  +z }
        #declare State[1][1][2] = object { A112 translate     +z }
        #declare State[2][1][2] = object { A212 translate +x  +z }

        // Unterste Ebene
        #declare State[0][0][0] = object { A000 translate -x-y-z }
        #declare State[1][0][0] = object { A100 translate   -y-z }
        #declare State[2][0][0] = object { A200 translate +x-y-z }
        #declare State[0][0][1] = object { A001 translate -x-y   }
        #declare State[1][0][1] = object { A101 translate   -y   }
        #declare State[2][0][1] = object { A201 translate +x-y   }
        #declare State[0][0][2] = object { A002 translate -x-y+z }
        #declare State[1][0][2] = object { A102 translate   -y+z }
        #declare State[2][0][2] = object { A202 translate +x-y+z }
#end

// Der Würfel wird mit 27 Standard-Atomen vorbelegt bzw. konfiguriert.
#macro Cube_Default(State)

        Cube_Config(State,

                Atom, Atom, Atom,
                Atom, Atom, Atom,
                Atom, Atom, Atom,

                Atom, Atom, Atom,
                Atom, Atom, Atom,
                Atom, Atom, Atom,

                Atom, Atom, Atom,
                Atom, Atom, Atom,
                Atom, Atom, Atom)
#end

// Anfangskonfiguration eines Zauberwürfels.
#macro Cube_Init(State, TexA, TexR, TexL, TexU, TexD, TexB, TexF)

        // Aufkleber erzeugen
        #local TagR = CreateTag(TexR, +x); // rechts
        #local TagL = CreateTag(TexL, -x); // links
        #local TagU = CreateTag(TexU, +y); // oben
        #local TagD = CreateTag(TexD, -y); // unten
        #local TagB = CreateTag(TexB, +z); // hinten
        #local TagF = CreateTag(TexF, -z); // vorne

        #local CenterAtom = object { Atom texture { TexA } }

        // Konfiguration eines gelösten Zauberwürfels
        Cube_Config(State,

                // Oberste Ebene
                CreateAtom3(TexA, TagL, TagU, TagF),
                CreateAtom2(TexA,       TagU, TagF),
                CreateAtom3(TexA, TagR, TagU, TagF),
                CreateAtom2(TexA, TagL, TagU      ),
                CreateAtom1(TexA,       TagU      ),
                CreateAtom2(TexA, TagR, TagU      ),
                CreateAtom3(TexA, TagL, TagU, TagB),
                CreateAtom2(TexA,       TagU, TagB),
                CreateAtom3(TexA, TagR, TagU, TagB),

                // Mittlere Ebene
                CreateAtom2(TexA, TagL,       TagF),
                CreateAtom1(TexA,             TagF),
                CreateAtom2(TexA, TagR,       TagF),
                CreateAtom1(TexA, TagL,           ),
                CenterAtom,
                CreateAtom1(TexA, TagR,           ),
                CreateAtom2(TexA, TagL,       TagB),
                CreateAtom1(TexA,             TagB),
                CreateAtom2(TexA, TagR,       TagB),

                // Unterste Ebene
                CreateAtom3(TexA, TagL, TagD, TagF),
                CreateAtom2(TexA,       TagD, TagF),
                CreateAtom3(TexA, TagR, TagD, TagF),
                CreateAtom2(TexA, TagL, TagD      ),
                CreateAtom1(TexA,       TagD      ),
                CreateAtom2(TexA, TagR, TagD      ),
                CreateAtom3(TexA, TagL, TagD, TagB),
                CreateAtom2(TexA,       TagD, TagB),
                CreateAtom3(TexA, TagR, TagD, TagB))
#end

// Ausgabe der konfigurierten 3x3x3-Matrix, Ausgabe des Zauberwürfels.
#macro Cube_Show(State)
        union {
                #local X = 0;
                #while (< 3)
                        #local Y = 0;
                        #while (< 3)
                                #local Z = 0;
                                #while (< 3)

                                        object { State[X][Y][Z] }

                                        #local Z = Z + 1;
                                #end
                                #local Y = Y + 1;
                        #end
                        #local X = X + 1;
                #end
        }
#end

// ----------------------------------------------------------------------------

/* Rotationen */

// x-Rotation: Eine y-z-Ebene (XIndex) wird um einen Winkel (Angle) gedreht.
#macro Cube_RotateX(State, XIndex, Angle)
        #if (Angle != 0.0) // höhere Effizienz
                #local X = XIndex;
                #local Y = 0;
                #while (< 3)
                        #local Z = 0;
                        #while (< 3)

                                #local State[X][Y][Z] = object {
                                        State[X][Y][Z]
                                        rotate x * Angle
                                }

                                #local Z = Z + 1;
                        #end
                        #local Y = Y + 1;
                #end
        #end
#end

// y-Rotation: Eine x-z-Ebene (YIndex) wird um einen Winkel (Angle) gedreht.
#macro Cube_RotateY(State, YIndex, Angle)
        #if (Angle != 0.0) // höhere Effizienz
                #local Y = YIndex;
                #local X = 0;
                #while (< 3)
                        #local Z = 0;
                        #while (< 3)

                                #local State[X][Y][Z] = object {
                                        State[X][Y][Z]
                                        rotate y * Angle
                                }

                                #local Z = Z + 1;
                        #end
                        #local X = X + 1;
                #end
        #end
#end

// z-Rotation: Eine x-y-Ebene (ZIndex) wird um einen Winkel (Angle) gedreht.
#macro Cube_RotateZ(State, ZIndex, Angle)
        #if (Angle != 0.0) // höhere Effizienz
                #local Z = ZIndex;
                #local X = 0;
                #while (< 3)
                        #local Y = 0;
                        #while (< 3)

                                #local State[X][Y][Z] = object {
                                        State[X][Y][Z]
                                        rotate z * Angle
                                }

                                #local Y = Y + 1;
                        #end
                        #local X = X + 1;
                #end
        #end
#end

// ----------------------------------------------------------------------------

/* Rotationssequenzen */

// Kernfunktion für Sequenzen, die einen Wert zwischen 0 und 1 ermittelt.
//   Anwendung: Für Animationen kann man für Value die Zeit (clock) einsetzen.
//   Voraussetzung: Start < End
//   Resultat:
//     * Falls (Value <= Start)      -> 0.0
//     * Falls (Value >= End)        -> 1.0
//     * Sonst (Start < Value < End) -> ((Value - Start) / (End - Start))
#declare interpolator = function(Start, End, Value) {
        min(1.0, max(0.0, (Value - Start) / (End - Start)))
}

// Turn: Eine Ebene (?Index) wird in Abhängigkeit von Value (z. B.: clock)
//   minimal um 0° (clock <= Start) und maximal um 90° (clock >= End) gedreht.
//   Ist clock >= End wirkt sich die Rotation auch auf die gegebene
//   3x3x3-Matrix (State) aus.
//   Der Spin {Negative = -1.0; Positive = +1.0} drückt die Drehrichtung aus.

// Konsistenz: Der Zustand des Würfels (State bzw. 3x3x3-Matrix) muss nach
//   abgeschlossener 90°-Drehung (Grafik-Drehung) ebenfalls mit einer adäquaten
//   Drehung (State-Drehung) angepasst werden, damit weitere Turns unabhängig
//   von den vorhergehenden Turns ausgeführt werden können.

// x-Turn: Eine y-z-Ebene (XIndex) wird konsistent gedreht (Grafik & State).
#macro Cube_TurnX(State, XIndex, Spin, Start, End, Value)

        #local X = XIndex;

        // Grafik-Drehung
        Cube_RotateX(State, X, 90.0 * Spin * interpolator(Start, End, Value))

        #if (Value >= End) // Abschlussbedingung -> State-Drehung

                #if (Spin > 0.0)

                        #local MemX00 = State[X][0][0];
                        #local State[X][0][0] = State[X][0][2];
                        #local State[X][0][2] = State[X][2][2];
                        #local State[X][2][2] = State[X][2][0];
                        #local State[X][2][0] = MemX00;

                        #local MemX01 = State[X][0][1];
                        #local State[X][0][1] = State[X][1][2];
                        #local State[X][1][2] = State[X][2][1];
                        #local State[X][2][1] = State[X][1][0];
                        #local State[X][1][0] = MemX01;

                #else

                        #local MemX00 = State[X][0][0];
                        #local State[X][0][0] = State[X][2][0];
                        #local State[X][2][0] = State[X][2][2];
                        #local State[X][2][2] = State[X][0][2];
                        #local State[X][0][2] = MemX00;

                        #local MemX01 = State[X][0][1];
                        #local State[X][0][1] = State[X][1][0];
                        #local State[X][1][0] = State[X][2][1];
                        #local State[X][2][1] = State[X][1][2];
                        #local State[X][1][2] = MemX01;

                #end
        #end
#end

// y-Turn: Eine x-z-Ebene (YIndex) wird konsistent gedreht (Grafik & State).
#macro Cube_TurnY(State, YIndex, Spin, Start, End, Value)

        #local Y = YIndex;

        // Grafik-Drehung
        Cube_RotateY(State, Y, 90.0 * Spin * interpolator(Start, End, Value))

        #if (Value >= End) // Abschlussbedingung -> State-Drehung

                #if (Spin > 0.0)

                        #local Mem0Y0 = State[0][Y][0];
                        #local State[0][Y][0] = State[2][Y][0];
                        #local State[2][Y][0] = State[2][Y][2];
                        #local State[2][Y][2] = State[0][Y][2];
                        #local State[0][Y][2] = Mem0Y0;

                        #local Mem1Y0 = State[1][Y][0];
                        #local State[1][Y][0] = State[2][Y][1];
                        #local State[2][Y][1] = State[1][Y][2];
                        #local State[1][Y][2] = State[0][Y][1];
                        #local State[0][Y][1] = Mem1Y0;

                #else

                        #local Mem0Y0 = State[0][Y][0];
                        #local State[0][Y][0] = State[0][Y][2];
                        #local State[0][Y][2] = State[2][Y][2];
                        #local State[2][Y][2] = State[2][Y][0];
                        #local State[2][Y][0] = Mem0Y0;

                        #local Mem1Y0 = State[1][Y][0];
                        #local State[1][Y][0] = State[0][Y][1];
                        #local State[0][Y][1] = State[1][Y][2];
                        #local State[1][Y][2] = State[2][Y][1];
                        #local State[2][Y][1] = Mem1Y0;

                #end
        #end
#end

// z-Turn: Eine x-y-Ebene (ZIndex) wird konsistent gedreht (Grafik & State).
#macro Cube_TurnZ(State, ZIndex, Spin, Start, End, Value)

        #local Z = ZIndex;

        // Grafik-Drehung
        Cube_RotateZ(State, Z, 90.0 * Spin * interpolator(Start, End, Value))

        #if (Value >= End) // Abschlussbedingung -> State-Drehung

                #if (Spin > 0.0)

                        #local Mem00Z = State[0][0][Z];
                        #local State[0][0][Z] = State[0][2][Z];
                        #local State[0][2][Z] = State[2][2][Z];
                        #local State[2][2][Z] = State[2][0][Z];
                        #local State[2][0][Z] = Mem00Z;

                        #local Mem10Z = State[1][0][Z];
                        #local State[1][0][Z] = State[0][1][Z];
                        #local State[0][1][Z] = State[1][2][Z];
                        #local State[1][2][Z] = State[2][1][Z];
                        #local State[2][1][Z] = Mem10Z;

                #else

                        #local Mem00Z = State[0][0][Z];
                        #local State[0][0][Z] = State[2][0][Z];
                        #local State[2][0][Z] = State[2][2][Z];
                        #local State[2][2][Z] = State[0][2][Z];
                        #local State[0][2][Z] = Mem00Z;

                        #local Mem10Z = State[1][0][Z];
                        #local State[1][0][Z] = State[2][1][Z];
                        #local State[2][1][Z] = State[1][2][Z];
                        #local State[1][2][Z] = State[0][1][Z];
                        #local State[0][1][Z] = Mem10Z;

                #end
        #end
#end

// ----------------------------------------------------------------------------

/* Mnemonik & Abstraktion */

#declare Positive = +1.0;
#declare Negative = -1.0;

#declare Center = 1;

#declare Left   = 0;
#declare Right  = 2;
#declare Up     = 2;
#declare Down   = 0;
#declare Front  = 0;
#declare Back   = 2;

// L  := Eine viertel Umdrehung der linken Ebene im Uhrzeigersinn.
#macro Cube_L (State, Start, End, Value)
        Cube_TurnX(State, Left, Negative, Start, End, Value)
#end

// L_ := Eine viertel Umdrehung der linken Ebene gegen den Uhrzeigersinn.
#macro Cube_L_(State, Start, End, Value)
        Cube_TurnX(State, Left, Positive, Start, End, Value)
#end

// R  := Eine viertel Umdrehung der rechten Ebene im Uhrzeigersinn.
#macro Cube_R (State, Start, End, Value)
        Cube_TurnX(State, Right, Positive, Start, End, Value)
#end

// R_ := Eine viertel Umdrehung der rechten Ebene gegen den Uhrzeigersinn.
#macro Cube_R_(State, Start, End, Value)
        Cube_TurnX(State, Right, Negative, Start, End, Value)
#end

// U  := Eine viertel Umdrehung der oberen Ebene im Uhrzeigersinn.
#macro Cube_U (State, Start, End, Value)
        Cube_TurnY(State, Up, Positive, Start, End, Value)
#end

// U_ := Eine viertel Umdrehung der oberen Ebene gegen den Uhrzeigersinn.
#macro Cube_U_(State, Start, End, Value)
        Cube_TurnY(State, Up, Negative, Start, End, Value)
#end

// D  := Eine viertel Umdrehung der unteren Ebene im Uhrzeigersinn.
#macro Cube_D (State, Start, End, Value)
        Cube_TurnY(State, Down, Negative, Start, End, Value)
#end

// D_ := Eine viertel Umdrehung der unteren Ebene gegen den Uhrzeigersinn.
#macro Cube_D_(State, Start, End, Value)
        Cube_TurnY(State, Down, Positive, Start, End, Value)
#end

// F  := Eine viertel Umdrehung der vorderen Ebene im Uhrzeigersinn.
#macro Cube_F (State, Start, End, Value)
        Cube_TurnZ(State, Front, Negative, Start, End, Value)
#end

// F_ := Eine viertel Umdrehung der vorderen Ebene gegen den Uhrzeigersinn.
#macro Cube_F_(State, Start, End, Value)
        Cube_TurnZ(State, Front, Positive, Start, End, Value)
#end

// B  := Eine viertel Umdrehung der hinteren Ebene im Uhrzeigersinn.
#macro Cube_B (State, Start, End, Value)
        Cube_TurnZ(State, Back, Positive, Start, End, Value)
#end

// B_ := Eine viertel Umdrehung der hinteren Ebene gegen den Uhrzeigersinn.
#macro Cube_B_(State, Start, End, Value)
        Cube_TurnZ(State, Back, Negative, Start, End, Value)
#end
Diese Framework−Datei besteht im Wesentlichen aus drei Teilen:
  1. dem Konfigurationsteil,
  2. dem Teil für Drehungen und
  3. dem Abstraktionsteil.
Ich beginne mit dem Konfigurationsteil, und zwar mit dem einfachen Makro Cube_Default.

develop.cube.default.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "cube.pov"

// Der Zustand des Zauberwürfels
#declare SolvedCube = array[3][3][3];

// Farblose Würfelkonfiguration mit Standardatomen
Cube_Default(SolvedCube)

// Ausgabe des Zauberwürfels
Cube_Show(SolvedCube)
Standardwürfel


Dieses Renderergebnis hat mir sehr bei der Justierung der Kamera und der Lichtquellen geholfen. Man sieht erstmals die Ausmaße eines Zauberwürfels.
Im Folgenden präsentiere ich Ihnen das Makro Cube_Init.

develop.cube.init.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "cube.pov"

// Der Zustand des Zauberwürfels
#declare SolvedCube = array[3][3][3];

// Anfangskonfiguration eines Zauberwürfels (Texturen aus develop.pov)
Cube_Init(SolvedCube, TA, TXP, TXN, TYP, TYN, TZP, TZN)

// Ausgabe des Zauberwürfels
Cube_Show(SolvedCube)
Der Zauberwürfel mit Anfangskonfiguration


Vorliegendes Framework bietet eine Menge Möglichkeiten zur Konfiguration, der Zauberwürfel kann immer noch – quasi atomar – umkonfiguriert werden. Wie das geht, verdeutlicht folgendes Beispiel unter Verwendung des Makros Cube_Config.

develop.cube.config.special.pov

#include "develop.pov"
#include "develop.mirrors.pov"

#include "cube.pov"

/* Spezialatome */

#declare Null = sphere { 0.0, 0.00001 }

#declare Corner = sphere { 0.0, 0.5 }

#declare EdgeZ = superellipsoid { <1, 0.25> scale 0.5 }
#declare EdgeY = object { EdgeZ rotate 90.0 * x }
#declare EdgeX = object { EdgeZ rotate 90.0 * y }

#declare Face = CreateAtom(0.25);

/* De-Textur */

#declare DeBlack = texture { pigment { color rgb <0.18, 0.18, 0.18> } }
#declare DeRed   = texture { pigment { color rgb <0.60, 0.11, 0.11> } }
#declare DeGold  = texture { pigment { color rgb <0.63, 0.55, 0.18> } }

#declare DeTexture = texture {
        gradient -y
        texture_map {
                [0.00 DeBlack]
                [0.27 DeBlack]
                [0.40 DeRed]
                [0.61 DeRed]
                [0.73 DeGold]
                [1.00 DeGold]
        }
        translate 1.5 * y
        scale 3.1 * y
}

#macro DeCube(State)
        #local X = 0;
        #while (< 3)
                #local Y = 0;
                #while (< 3)
                        #local Z = 0;
                        #while (< 3)

                                #declare State[X][Y][Z] = object {
                                        State[X][Y][Z]
                                        texture { DeTexture }
                                }

                                #local Z = Z + 1;
                        #end
                        #local Y = Y + 1;
                #end
                #local X = X + 1;
        #end
#end

/* De-Würfel */

#declare SpecialCube = array[3][3][3];

Cube_Config(SpecialCube,

        Corner, EdgeX, Corner,
        EdgeZ , Face , EdgeZ ,
        Corner, EdgeX, Corner,

        EdgeY , Face , EdgeY ,
        Face  , Null , Face  ,
        EdgeY , Face , EdgeY ,

        Corner, EdgeX, Corner,
        EdgeZ , Face , EdgeZ ,
        Corner, EdgeX, Corner)

DeCube(SpecialCube)

Cube_Show(SpecialCube)
Mein Deutschlandwürfel


Ich hoffe, ich bin jetzt bei Ihnen nicht unten durch, weil ich die Deutschlandfarben verwende — a bissale Nationalstolz muas a moi sai! :−)
Klar, wenn man etwas konfiguriert, möchte man es auch anzeigen. Dazu dient das Makro Cube_Show.
Und nun zum Teil der Drehungen: Die Rotationsmakros sind lediglich Hilfsmakros für die Rotationssequenzen, die ich Turns genannt habe. Mit den Turns kann man unabhängige Drehungen vollziehen. Dazu folgendes kleines Beispiel, welches gleichzeitig den ersten Test der Turnmakros darstellt:

develop.cube.anixyz.pov

#include "develop.pov"
#include "develop.axes.pov"
#include "develop.mirrors.pov"

#include "cube.pov"

#declare MyCube = array[3][3][3];

Cube_Init(MyCube, TA, TXP, TXN, TYP, TYN, TZP, TZN)

// Sequenz für Blume

Cube_TurnX(MyCube, Left,   Negative, 0.20, 0.30, clock)
Cube_TurnX(MyCube, Right,  Negative, 0.25, 0.30, clock)
Cube_TurnZ(MyCube, Center, Negative, 0.40, 0.50, clock)
Cube_TurnX(MyCube, Left,   Positive, 0.60, 0.70, clock)
Cube_TurnX(MyCube, Right,  Positive, 0.65, 0.70, clock)
Cube_TurnY(MyCube, Up,     Positive, 0.80, 0.90, clock)
Cube_TurnY(MyCube, Down,   Positive, 0.85, 0.90, clock)

// Rücksequenz

Cube_TurnX(MyCube, Left,   Negative, 1.20, 1.30, clock)
Cube_TurnX(MyCube, Right,  Negative, 1.25, 1.30, clock)
Cube_TurnZ(MyCube, Center, Positive, 1.40, 1.50, clock)
Cube_TurnX(MyCube, Left,   Positive, 1.60, 1.70, clock)
Cube_TurnX(MyCube, Right,  Positive, 1.65, 1.70, clock)
Cube_TurnY(MyCube, Up,     Negative, 1.80, 1.90, clock)
Cube_TurnY(MyCube, Down,   Negative, 1.85, 1.90, clock)

Cube_Show(MyCube)
Erste Zauberwürfelanimation des Frameworks


Nebenbei bemerkt: Diese Drehsequenz endet in einem Blumenmuster (engl.: six spot).
Die Anwendung des Abstraktionsteiles von cube.pov finden Sie im Folgenden in der dritten Framework−Datei patterns.pov oder auf der Test- und Tutoriumseite POV−Ray−Zauberwürfel−Tests.

Generierung der Animation

Mit dem POV−Ray−Script develop.cube.anixyz.pov erhält man jedoch noch keine Animationsdatei. Sie benötigen dazu noch folgende INI−Datei, mit der der POV−Ray−Renderer 89 PNG−Dateien generiert, sowie ein Programm zum Zusammenführen dieser Dateien (hier ImageMagick).

develop.cube.anixyz.ini

Input_File_Name=develop.cube.ani.xyz.pov

Output_File_Type=N

Bits_Per_Color=3

Width=200
Height=150

Antialias=On
Antialias_Threshold=0.001
Antialias_Depth=3

Jitter=Off

Initial_Frame=0
Final_Frame=88
Initial_Clock=0
Final_Clock=2
Nach dem der POV−Ray−Renderer die besagten 89 Dateien generiert hat, können Sie diese zu einer einzelnen Animationsdatei zusammenführen. Ich verwende dazu das Konsolenprogramm convert von ImageMagick etwa wie folgt:

Verwendung von ImageMagick

convert
  -quality 100
  -delay 12
  -layers trim-bounds
  -layers optimize-transparency
  .\develop.cube.ani.xyz??.png
  .\develop.cube.ani.xyz.gif
Sie erhalten folgende Animation:
Erste Zauberwürfelanimation des Frameworks





Drehsequenzen

Sammlung schöner Drehsequenzen

Selbstverständlich ist dieses Framework nur dann sinnvoll, wenn man auch lange Abfolgen von Drehsequenzen definieren kann. Ich habe mir eine kleine Sammlung von Drehsequenzen in der vorerst letzten Framework−Datei patterns.pov zusammengestellt.

patterns.pov

/* Framework: POV-Ray-Zauberwürfel von Stefan K. Baur */

#include "cube.pov"

// ----------------------------------------------------------------------------

/* Hilfsmakro */

#macro Intervals(Count, StartArray, EndArray, Start, End)

        #local Delta = (End - Start) / Count;

        #local I = 0;
        #while (< Count)

                #local StartArray[I] = Start + I * Delta;
                #local EndArray[I]   = StartArray[I] + 0.75 * Delta;

                #local I = I + 1;
        #end
#end

// ----------------------------------------------------------------------------

/* Patterns */

// Pattern: "Pons Asinorum" ~ "Eselsbrücke" (12 Turns)
#macro Pattern_PonsAsinorum(State, Start, End, Value)

        #local Count = 12;

        #local SA = array[Count];
        #local EA = array[Count];

        Intervals(Count, SA, EA, Start, End)

        Cube_F (State, SA[ 0], EA[ 0], Value)
        Cube_F (State, SA[ 1], EA[ 1], Value)
        Cube_B (State, SA[ 2], EA[ 2], Value)
        Cube_B (State, SA[ 3], EA[ 3], Value)
        Cube_R (State, SA[ 4], EA[ 4], Value)
        Cube_R (State, SA[ 5], EA[ 5], Value)
        Cube_L (State, SA[ 6], EA[ 6], Value)
        Cube_L (State, SA[ 7], EA[ 7], Value)
        Cube_U (State, SA[ 8], EA[ 8], Value)
        Cube_U (State, SA[ 9], EA[ 9], Value)
        Cube_D (State, SA[10], EA[10], Value)
        Cube_D (State, SA[11], EA[11], Value)
#end

// Pattern: "Six Spot" ~ "Blume" (8 Turns)
#macro Pattern_SixSpot(State, Start, End, Value)

        #local Count = 8;

        #local SA = array[Count];
        #local EA = array[Count];

        Intervals(Count, SA, EA, Start, End)

        Cube_U (State, SA[0], EA[0], Value)
        Cube_D_(State, SA[1], EA[1], Value)
        Cube_R (State, SA[2], EA[2], Value)
        Cube_L_(State, SA[3], EA[3], Value)
        Cube_F (State, SA[4], EA[4], Value)
        Cube_B_(State, SA[5], EA[5], Value)
        Cube_U (State, SA[6], EA[6], Value)
        Cube_D_(State, SA[7], EA[7], Value)
#end

// Pattern: "Cube in a Cube" ~ "Würfel in Würfel" (18 Turns)
#macro Pattern_CubeInACube(State, Start, End, Value)

        #local Count = 18;

        #local SA = array[Count];
        #local EA = array[Count];

        Intervals(Count, SA, EA, Start, End)

        Cube_F (State, SA[ 0], EA[ 0], Value)
        Cube_L (State, SA[ 1], EA[ 1], Value)
        Cube_F (State, SA[ 2], EA[ 2], Value)
        Cube_U_(State, SA[ 3], EA[ 3], Value)
        Cube_R (State, SA[ 4], EA[ 4], Value)
        Cube_U (State, SA[ 5], EA[ 5], Value)
        Cube_F (State, SA[ 6], EA[ 6], Value)
        Cube_F (State, SA[ 7], EA[ 7], Value)
        Cube_L (State, SA[ 8], EA[ 8], Value)
        Cube_L (State, SA[ 9], EA[ 9], Value)
        Cube_U_(State, SA[10], EA[10], Value)
        Cube_L_(State, SA[11], EA[11], Value)
        Cube_B (State, SA[12], EA[12], Value)
        Cube_D_(State, SA[13], EA[13], Value)
        Cube_B_(State, SA[14], EA[14], Value)
        Cube_L (State, SA[15], EA[15], Value)
        Cube_L (State, SA[16], EA[16], Value)
        Cube_U (State, SA[17], EA[17], Value)
#end

Animationsfilme

Anmerkung

Selbstverständlich appelliere ich wieder an Ihren Ehrgeiz, vorliegendes Framework selbständig zu erweitern.
Herzlichst
Stefan Karl Baur



Info

stefan−baur.de / POV−Ray−Zauberwürfel
  • besucht am Freitag, den 12. März 2010 um 3:41 Uhr
  • geändert am Sonntag, den 15. März 2009 von Stefan K. Baur
  • ähnliche Seiten:






Startseite

Copyright © 2004-2009 Stefan K. Baur − Druck20042005200620072008200920102011