Mehrere 3D-"Flächenmodelle" zeichnen und animieren
Die fünf "Platonischen Körper" (Tetraeder, Würfel, Oktaeder,
Dodekaeder und Ikosaeder) umkreisen den "Buckyball"
Rotation überlagern:    

Buckyball und platonische Körper

An dem nebenstehend zu sehenden Bild soll demonstriert werden,

Die Realisierung all dieser Punkte ist nicht annähernd so schwierig, wie man erwarten könnte. Allerdings scheidet die Möglichkeit aus, nacheinander Transformationen für die einzelnen Körper zu setzen und dann mit Funktionen zu zeichnen, die die Transformationen auswerten. Prinzipiell ist das zwar auch bei mehreren Körpern möglich, aber wenn man korrekte Darstellung der Überdeckung erzielen möchte, muss zunächst aus mehreren Körpern ein einziges Modell erzeugt werden.

Folgende Strategie wird verfolgt: Die auf der Seite "3D-Flächenmodelle in geeigneter Reihenfolge zeichnen" vorgestellte und ausführlich erläuterte Funktion draw, die ein mit den beiden Arrays xyz und area beschriebenes Modell zeichnet, wird unverändert auch hier genutzt, indem vorab die Arrays, die die einzelnen Körper beschreiben, zu einem Array xyz und einem Array area zusammengeführt werden. Dafür wird ein Array models definiert, das die Beschreibungen der zu verwendenden Körper mit zusätzlichen Parametern enthält, und die Funktion mergemodels führt die Modellbeschreibungen zu einem Modell zusammen:

      var models = [[cubexyz   , cubearea   , -6 , -4 , 0 , 1.8 , 2] ,  // Fuer jedes Modell:
                    [dodekaxyz , dodekaarea ,  8 ,  0 , 0 , 1.5 , 2] ,  // Koordinaten-Array,
                    [buckyxyz  , buckyarea  ,  0 ,  0 , 0 , 3   , 0] ,  // Flaechen-Array,
                    [oktaxyz   , oktaarea   ,  2 , -8 , 0 , 2   , 0] ,  // x-, y- und z-Offset,
                    [ikosaxyz  , ikosaarea  ,  2 ,  8 , 0 , 1   , 1] ,  // Skalierungsfaktor,
                    [tetraxyz  , tetraarea  , -6 ,  4 , 0 , 2   , 1]] ; // Achse der Eigendrehung

      function mergemodels () {

         var jjx = 0 , jja = 0 , ptoffs = 0 ;
         for (var i = 0 ; i < models.length ; i++) {            // ... ueber alle Modelle
            for (var j = 0 ; j < models[i][0].length ; j++) {   // ... ueber alle Punkte eines Modells
               xyz[jjx] = models[i][0][j].concat() ;            // ... ein Punkt wird kopiert
               gi.w2wtransform (xyz[jjx] , models[i][2] , models[i][3] , models[i][4] ,
                                 psi , models[i][6] , models[i][5]) ;
               jjx++ ;
            }
            for (var j = 0 ; j < models[i][1].length ; j++) {   // ... ueber alle Flaechen eines Modells
               area[jja] = models[i][1][j].concat() ;           // ... eine Flaeche wird kopiert
               for (var k = 0 ; k < area[jja].length - 1 ; k++) {
                  area[jja][k] += ptoffs ;                      // ... + Offset der Punktnummern
               }                                                //     (Punkte haben neue Nummern)
               jja++ ;
            }
            ptoffs += models[i][0].length ;                     // ... += "Anzahl der Punkte des Modells"
         }
         psi += dPhi ;                                          // ... Winkel fuer Eigendrehung
      }

Mit dem Aufbau der beiden Arrays xyz und area, die nun ein einziges Modell beschreiben, gilt alles, was auf den Seiten "3D-Drahtmodelle zeichnen und animieren" und "3D-Flächenmodelle in geeigneter Reihenfolge zeichnen" ausführlich beschrieben wird. Deshalb kann der nachfolgende Überblick über den Rest der hier beschriebenen Grafik kurz ausfallen:

      function init () {

         gi = new canvasGI ("canvas") ;            // Ein "CanvasGI-Objekt" wird erzeugt
         mergemodels () ;                          // ... merged die Modelle
         var limits = gi.ptlimits (xyz) ;          // ... berechnet "User coorniates"-Limits
         gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 10) ;
         phiplus (gi.AXIS_Z) ;                     // ... startet Animation (Rotation um vertikale Achse)
      }
      function phiplus (axis) {
         rotax = axis ;
         if (anim == 1) {
            window.clearInterval (aktiv) ;
         }
         anim  = 1    ;
         aktiv = window.setInterval("rotation ()" , dT);
      }
      function rotation () {
         gi.t3rotation  (dPhi , rotax) ;
         draw () ;
      }

      function draw () {

         mergemodels () ;                               // ... merged die Modelle

         var points = new Array () ;                    // ... fuer die Koordinaten eines Polygons
         gi.clearcanvas ("silver" , "black") ;          // ... fuellt Canvas-Bereich mit Hintergrundfarbe
                                                        //     ("silver") und zeichnet schwarzen Rahmen
         for (var i = 0 ; i < area.length ; i++) {
            var np = area[i].length - 1 ;               // ... weil letztes Element die Farbe ist
            for (var j = 0 ; j < np ; j++) {
               points[j] = xyz[area[i][j]] ;            // ... sammelt Punktkoordinaten eines Polygons
            }
            area[i].unshift (gi.wtsymbdist (points , np)) ;  // ... berechnet symbolischen Abstand vom Betrachter
         }                                              //          und fuegt diesen Wert an der Array-Spitze an

         gi.sortbyindex (area , 0) ;                    // ... sortiert nach den Werten an der Array-Spitze

         for (var i = 0 ; i < area.length ; i++) {      // ... zeichnet alles in der korrekten Reihenfolge
            area[i].shift() ;                           // ... entfernt den Wert an der Array-Spitze wieder
            var np = area[i].length - 1 ;
            for (var j = 0 ; j < np ; j++) {
               points[j] = xyz[area[i][j]] ;            // ... sammelt Punktkoordinaten eines Polygons
            }
            gi.wtdrawfarea (points , np , area[i][np] , "black") ; // ... zeichnet ein einzelnes Polygon
         }
      }

Die oben zu sehende Grafik kann auch als eigenständige Seite "Platonische Körper und Buckyball" aufgerufen werden.

Weiterlesen