WaBis

walter.bislins.ch

JSG: Anwendung

Diese Beschreibung setzt voraus, dass du die Grundlagen gelesen hast.

Kopiere die Dateien x.js und jsg.js in ein beliebiges Verzeichnis. Für die folgende Beschreibung wird angenommen, dass sich die Datei im selben Verzeichnis wie die Webseite befindet.

Wenn ein Canvas fixer Grösse verwendet wird, ist die Anwendung des JSG-Moduls besonders einfach. Soll sich die Canvas-Grösse dynamisch an die Fenstergrösse des Browsers anpassen, wird die Anwendung etwas komplizierter, weil bei einer Grössenänderung die Grafik neu gezeichnet werden muss.

Canvas mit fixer Grösse

Da bei einem Canvas fixer Grösse der Browser das Neuzeichnen der Grafik übernimmt, zum Beispiel wenn ein Teil davon durch ein anderes Fenster verdeckt war, ist da Anwendung besonders einfach. Folge einfach den folgenden vier Schritten:

1. JavaScripts includen

<script src="jsgX.js" type="text/javascript"></script>

Die Datei jsgX.js enthält die beiden Dateinen x.js und jsg.js in minimierter Form.

2. Canvas erzeugen

Erzeuge an einer beliebigen Stelle auf der Webseite ein Canvas-Element über den folgenden JavaScript Aufruf:

var jsg = NewGraph2D( { 
  Id = 'myGraph', Width: 600, Height: 400, 
  BorderWidth: 1, BorderColor: 'blue' 
} );

Damit wird ein Canvas-Element mit der Id=myGraph, der Breite 600 Pixel, der Höhe 400 Pixel und mit einem blauen Rahmen der Dicke 1 Pixel an dieser Stelle erzeugt. Dieses Element ist nun unsere Zeichenfläche.

3. Viewport und Window definieren

Für die Abbildungstransformation müssen nun der Viewport- und Window-Bereich festgelegt werden. Die folgenden JavaScript-Anweisungen können nun an einer beliebigen Stelle der Seite stehen. Es muss nur garantiert werden, dass sie nach dem Erzeugen des Canvas ausgeführt werden:

jsg.SetViewport( 25, 20, 565, 360 );
jsg.SetWindow( -1.05, -1.05, 1.05, 1.05 );

4. Attribute setzen und Zeichnen

Nun können Attribute wie Linienfarbe und -dicke gesetzt werden und es kann Grafik gezeichnet werden:

jsg.SetLineAttr( 'red', 3 );
jsg.Line( -1, -1, 1, 1 );
 :

Zeichnet eine rote, 3 Pixel dicke Linie von (-1,-1) nach (1,1) in Window-Koordinaten.

Es darf jederzeit ein neuer Viewport- oder Window-Bereich definiert werden. Es können auch beliebig viele Canvas auf einer Seite erzeugt werden. Es gibt einige nützliche Funktionen zum Zeichnen und Beschriften von Gitternetzen und Achsen, sowie zum setzen von Markierungen (siehe Demo).

Canvas mit variabler Grösse

Gibt man beim Erzeugen eines Canvas die Grösse in Prozent an, so passt sich die Grösse des Canvas automatisch an die Grösse des Browserfensters an, auch wenn diese nachträglich verändert wird. Weil dabei jedoch auch die Grösse der Grafik ändert, muss diese neu gezeichnet werden. Dazu wurde im JSG-Modul ein sog. Callback-Mechanismus eingebaut, der wiefolgt funktioniert:

Der Callback-Mechanismus

Alle Zeichenoperationen werden in eine Funktion verpackt. Diese Funktion wird beim Erzeugen des Canvas als Argument übergeben oder später mit SetDrawFunc() an den Canvas gekoppelt. Der Canvas ruft diese Funktion automatisch auf, immer dann, wenn der Canvas-Inhalt neu gezeichnet werden muss.

Nun kann es passieren, dass während dem Zeichnen die Grösse des Canvas geändert wird. Um in diesem Fall eine vollständige und korrekte Grafik zu erhalten, muss das Zeichnen synchronisiert werden. Dies geschieht dadurch, dass am Anfang der Zeichenfunktion BeginDrawing() und am Ende EndDrawing() gerufen wird. Solange sich die Programmausführung zwischen diesen beiden Aufrufen befindet, wird ein erneuter Redraw unterbunden und für einen späteren Zeitpunkt in eine Warteschleife gesetzt. Diese beiden Funktionen werden intern automatisch gerufen, bevor und nach dem Aufruf der installierten Draw-Funktion, sodass diese dort nicht gerufen werden müssen.

Einfaches Beispiel mit variablem Canvas

Das folgende Beispiel zeigt exemplarisch, wie eine Anwendung mit einem variabel grossem Canvas auszusehen hat. Es spricht nichts dagegen, auch eine Anwendung mit fixem Canvas so zu programmieren.

var jsg = NewGraph2D( { Width: '100%', Height: '75%', DrawFunc: Draw } );

function Draw(g) {
  // hier deine Zeichenanweisungen
}

Das Objekt jsg wird der Funktion Draw als Argument übergeben, sodass innerhalb von Draw nicht auf das globale Objekt zugegriffen werden muss.

Die Funktion Draw wird erst aufgerufen, wenn die Seite geladen ist und der Canvas seine Grösse entsprechend den Vorgaben berechnet und gesetzt hat.

Beachte, dass alle nicht definierten Parameter beim Aufruf von NewGraph2D Standardwerte erhalten.

Beispiel mit länger dauernden Grafikausgaben

Komplexe länger dauernde Grafikfunktionen können mit der Funktion IsInvalidDrawing() von Zeit zu Zeit abfragen, ob die Canvasgrösse eventuell in der Zwischenzeit geändert hat und die aktuelle Ausgabe daher abgebrochen werden kann. Das JSG-Modul ignoriert intern alle Grafikausgaben, wenn der Canvas ungültig geworden ist und löst nach dem (internen) Aufruf von EndDrawing() erneut einen Draw aus. Damit wird nicht unnötig Zeit für das Zeichnen von Grafik verschwendet, die sowieso im nächsten Moment neu gezeichnet werden muss. Das funktioniert auf diese Weise sogar selbst dann, wenn die Zeichenfunktion IsInvalidDrawing() nicht verwendet wird.

var jsg = NewGraph2D( { DrawFunc: Draw } );

function Draw(g) {
  :
  // Schleife mit komplizierten Berechnungen
  while (!finished) {
    // Zeichenanweisungen
    :
    if (g.IsInvalidDrawing()) {
      // Zeichnen hier abbrechen
      return;
    }
  }
  :
}

Pixelgrössen berechnen bei dynamischen Canvas

Der Vorteil von Canvas fixer Grösse ist, dass Beschriftungen in einer festen Pixelgrösse angegeben werden können. Bei Canvas mit dynamischer Grösse ist es wünschenswert, wenn Ränder, Marker- und Schriftgrössen mit der Grösse des Canvas skalieren. Dies geschieht nicht automatisch. Es gibt jedoch eine Reihe Funktionen, die bei diesem Problem helfen:

function Draw( jsg ) {
  jsg.SetScaleRef( 400 );
   :
  jsg.SetViewport( jsg.ScalePixI(25), jsg.ScalePixI(20), -jsg.ScalePixI(10), -jsg.ScalePixI(10) );
   :
  jsg.SetTextSize( jsg.ScalePixI(12) );
   :
}

Mit Hilfe der Funktion ScalePixI() kann man nun die Zeichenfunktion programmieren, als wenn der Canvas eine fixe Grösse hätte, wie bei SetScaleRef() angegeben. Man muss nur einfach alle Pixel-Werte über eine der Scale-Funktionen umrechnen lassen.

Die Funktion ScalePix() berechnet einen Wert vom Typ Float, während die Funktion ScalePixI() immer einen ganzen Integer zurück gibt, der mindestens 1 ist.

Ersetze den Wert 400 in der Funktion SetScaleRef() durch die Standard-Breite des Canvas in Pixeln, die er haben würde, wenn du einen fixen Canvas verwenden würdest. Hat der flexible Canvas tatsächlich diese Breite, so haben die Funktionen ScalePix() und ScalePixI() keine Wirkung; sie geben genau den Wert zurück, der als Argument übergeben wird. Ist der Canvas jedoch kleiner als Standard-Breite, werden entsprechend kleinere Werte zurückgegeben. Ist der Canvas grösser werden grössere Werte zurückgegeben. Die Grafik mit allen Beschriftungen und Abständen in Pixeln skalieren schön mit dem Canvas.

Berechnung einer praktischen Window-Grösse

Will man eine Grafik zeichnen, deren Defintions- oder Wertebereich nicht fest vorgegeben ist, so muss man die Window-Grösse aufgrund der berechneten Daten ermitteln. Dabei entsteht das Problem, dass Beschriftungen sich eventuell überlappen, wenn nicht genug Leerraum eingeplant wird. Das JSG-Modul ist in der Lage, Achsenbeschriftungen selbständig auszudünnen, sodass sie sich nie überlappen. Am Ende einer Achse benötigt man oft etwas Platz für die Beschriftung der Einheiten. Wie viel Platz soll man Reservieren? Das hängt vom Wertebereich der Grafik ab.

Die Funktion ScaleToTic() berechnet den erweiterten Wertebereich für ein Window, wenn der Wertebereich der Grafik bekannt ist:

function Draw(g) {
  // Berechnung der Grafik 
  var xValues = [], yValues = [];
  ComputeValues( xValues, yValues );
  // Berechnung des Wertebereiches
  var xMax = MaxOfArray( xValues );
  var yMax = MaxOfArray( yValues );
  // Berechnung Window Bereich
  var xTic = 10;
  var yTic = 50;
  var xWinMin = 0;
  var xWinMax = g.ScaleToTic( xMax, xTic );
  var yWinMin = 0;
  var yWinMax = g.ScaleToTic( yMax, yTic );
  // Window setzen
  g.SetWindow( xWinMin, yWinMin, xWinMax, yWinMax );
  :
}

Weitere Infos zur Seite
Erzeugt Dienstag, 2. Februar 2016
von wabis
Zum Seitenanfang
Geändert Dienstag, 2. Februar 2016
von wabis