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" }
gi.w2wtransarray (xyz , ptoffs) ;
werden die zuletzt in das Array xyz (ab Position ptoffs) eingebrachten Koordinatentripel mit der eingestellten Transformation modifiziert.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:
<body onLoad="init();">
gestartet, die folgende Funktion aufruft: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 } }
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" |
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 () ; }
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"