Modell mehrfach kopieren, "Zoom and Move"

Auf dieser Seite werden zwei CanvasGI-Techniken vorgestellt:

Zoom Verschiebung:
     Skalierung von bis

Modell mehrfach darstellen

In dem nebenstehend zu sehenden Bild wird ein einziges Modell (symbolischer "Baum", bestehend aus 73 Flächen: Dreiecke, Vierecke und Achtecke) unter Verwendung unterschiedlicher Transformationen insgesamt 100-mal dargestellt. Prinzipiell sind zwei Strategien möglich:

Während auf der Seite "Projektionen und 3D-Transformationen" die Funktionen zur Einstellung einer Transformation beschrieben werden, die inkrementell arbeiten (eine neue Transformation wird der gerade gültigen Transformation überlagert), soll hier eine "absolut" arbeitende Transformation vorgestellt werden, die alle bisherigen Transformationseinstellungen ignoriert. Mit

t3transabs (tx , ty , tz , phi , axis , sx , sy , sz)

wird eine Transformation eingestellt, die in folgender Reihenfolge arbeitet:

Alle vorab eingestellten Transformationen bleiben unberücksichtigt (und sind damit verschwunden).

In der Funktion combine werden alle transformierten Modelle tatsächlich erzeugt, so dass alle Flächen aller Modelle mit den Arrays xyz und area beschrieben werden:

      var nx = 10 ;
      var ny = 10 ;
      var xstart = 0 ;
      var deltax = 100 ;
      var ystart = 0 ;
      var deltay = 100 ;
      var scalmin = 1 ;
      var scalmax = 1 ;
      var limits = { xumin:0 , xumax:1 , yumin:0 , yumax:1} ;
      var xyz  = new Array () ;                       // ... fuer Koordinaten der Punkte aller Modelle
      var area = new Array () ;                       // ... fuer Flaechen aller Modelle

      var model = [treexyz , treearea] ;              // ... Modell, das aus flaechenmodelle.js
                                                      //     verwendet werden soll
      function combine () {

         var jjx = 0 , jja = 0 , ptoffs = 0 ;
         for (var ix = 0 ; ix < nx ; ix++) {
            for (var iy = 0 ; iy < ny ; iy++) {
               var scal = (scalmax - scalmin) * Math.random() + scalmin ; // Skalierungsfaktor
               for (var j = 0 ; j < model[0].length ; j++) {   // ... ueber alle Punkte des Modells
                  xyz[jjx++] = model[0][j].concat() ;          // ... ein Punkt wird (!)kopiert(!),
               }                                               //     nicht referenziert
               gi.t3transabs (xstart+ix*deltax , ystart+iy*deltay , 0 , 0 , 0 , scal , scal , scal) ;
               gi.w2wtransarray (xyz , ptoffs) ;

               for (var j = 0 ; j < model[1].length ; j++) {   // ... ueber alle Flaechen des Modells
                  area[jja] = model[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 += model[0].length ;                     // ... += "Anzahl der Punkte des Modells"

            }    // for (var iy ...
         }       // for (var ix ...
         gi.tinit () ;                                         // ... stellt alle Transformation wieder "auf Null"
      }

Mit dem Aufbau der beiden Arrays xyz und area, die nun ein einziges Modell beschreiben, gilt alles, was auf den Seiten "3D-Flächenmodelle in geeigneter Reihenfolge zeichnen" und "Mehrere 3D-Flächenmodelle zeichnen und animieren" 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
         combine () ;                              // ... merged die Modelle
         limits = gi.ptlimits (xyz) ;              // ... berechnet "User coorniates"-Limits
         gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 0) ;
         draw () ;
      }
      function draw () {

         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.wdrawfarea (points , np , area[i][np] , "black") ; // ... zeichnet ein einzelnes Polygon
         }
      }

"Baumschule" und "Wald"

Beim Start dieser Seite zeigt die Grafik oben rechts das Bild als "Baumschule" (alle Bäume haben die gleichen Größe). Mit Klick auf den Button "Wald" wird die Funktion Wald gestartet:

               <input type="button" value="Wald" name="Wd"
                      style="width:75px; height:27px; font-size:small;" onclick="Wald ();" />
               Skalierung von
               <input type="text" name="Scalmin" value="0.5" size="1" />
               bis
               <input type="text" name="Scalmax" value="1.5" size="1" />

In der Funktion Wald werden zunächst die beiden neben dem Button liegenden Input-Felder ausgelesen.

      function Wald () {

          scalmin = parseFloat (document.eingabe.Scalmin.value) ;
          scalmax = parseFloat (document.eingabe.Scalmax.value) ;
          combine () ;                              // ... merged die Modelle
          Fit () ;
      }
Zufallsskalierungen machen aus der "Baumschule" einen "Wald"

"Zoom and Move"

Für "Zoom and Move" existieren unterhalb der oben auf dieser Seite zu sehenden Zeichenfläche insgesamt 7 Buttons. Man sollte sie ausprobieren, bevor man die nachfolgenden Erläuterungen liest.

Alle Reaktionen auf das Anklicken eines Buttons werden mit der Funktion setusercoordsi realisiert. Diese Funktion wird auf der Seite "Problembezogene Koordinatensysteme (User coordinates)" ausführlich beschrieben. Mit ihr werden die Grenzen der 2D-Koordinaten in der Zeichenfläche eingestellt. In der oben beschriebenen Funktion init wird sie in Verbindung mit der Funktion ptlimits verwendet:

               limits = gi.ptlimits (xyz) ;
               gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 0) ;

Das Anklicken eines "Zoom-Buttons" löst den Aufruf einer der beiden Funktionen Zoominout bzw. Fit aus:

               <input type="button" value=".. in" name="Zoomin"
                 style="width:50px; height:27px; font-size:small;" onclick="Zoominout (0.1);" />
               <input type="button" value="... out" name="Zoomout"
                 style="width:50px; height:27px; font-size:small;" onclick="Zoominout (-0.1);" />
               <input type="button" value="... fit" name="WindowFit"
                 style="width:50px; height:27px; font-size:small;" onclick="Fit() ;" />

Die beiden Funktionen sind weitgehend selbsterklärend. Zoominout modifiziert die Grenzen für die 2D-Zeichnung, Fit berechnet die Grenzen neu, und in beiden Fällen wird mit draw die Zeichnung erneuert:

      function Zoominout (fac) {

          var width  = limits.xumax - limits.xumin ;
          var height = limits.yumax - limits.yumin ;
          limits.xumin += width  * fac ;
          limits.xumax -= width  * fac ;
          limits.yumin += height * fac ;
          limits.yumax -= height * fac ;
          gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 0) ;
          draw () ;
      }

      function Fit () {

         limits = gi.ptlimits (xyz) ;              // ... berechnet "User coorniates"-Limits
         gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 0) ;
         draw () ;
      }

Die Buttons, mit denen die "Move"-Funktionen gestartet werden, rufen eine der beiden Funktionen xmove bzw. ymove auf:

               <input type="button" value="→" name="MoveRight"
                 style="width:30px; height:27px; font-size:small;" onclick="xmove(0.1) ;" />
               <input type="button" value="←" name="MoveLeft"
                 style="width:30px; height:27px; font-size:small;" onclick="xmove(-0.1) ;" />
               <input type="button" value="↑" name="MoveUp"
                 style="width:30px; height:27px; font-size:small;" onclick="ymove(0.1) ;" />
               <input type="button" value="↓" name="MoveDown"
                 style="width:30px; height:27px; font-size:small;" onclick="ymove(-0.1) ;" />

Auch diese beiden Funktionen sind selbsterklärend:

      function xmove (fac) {

          var width  = limits.xumax - limits.xumin ;
          limits.xumin -= width  * fac ;
          limits.xumax -= width  * fac ;
          gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 0) ;
          draw () ;
      }

      function ymove (fac) {

          var heigth  = limits.yumax - limits.yumin ;
          limits.yumin -= heigth  * fac ;
          limits.yumax -= heigth  * fac ;
          gi.setusercoordsi (limits.xumin , limits.yumin , limits.xumax , limits.yumax , 0) ;
          draw () ;
      }

Beipiel-Programm "Modell kopieren, 'Zoom and Move'"

Das Beipiel-Programm "Modell kopieren, 'Zoom and Move'" enthält neben den oben beschriebenen Funktionen noch wesentlich mehr Angebote. Es startet mit folgendem Bild:

Bilder, die mit dem Beispiel-Programm erzeugt wurden:

    

"Buckyball-Teppich" und "1000 Würfel"

Weiterlesen