IMPRESS dotted_line CONTACT dotted_line search dotted_line Contact dotted_line sitemap dotted_line print
266 PHP-Resource users online

Switch to another languags Deutsch aktuelle Sprache Englisch
1&1 Webhosting
- Ad -
php-resource

plotting masters - a professional guide - part II + III

Grafische Interpolation und Bestapproximation von numerischen Wertepaaren. Mit anderen Worten: Wir wollen Punkte auf einer Zeichenebene über verschiedene Verfahren miteinander verbinden. Die beispielorientierte Anwendung (Teil I) ist einfach und für jeden verständlich. Die formale Umsetzung (Teil II + III) sowie das individuelle Erweiterungspotential richtet sich hingegen an fortgeschrittene Programmierer.

|2009.09.12 | EVAMasters | 9985 | KAT : PHP | | Comments 2


1

4
                    Herkömmliche Programmiertechnik
                    mit PHP


                    1. Fassung Berlin Jun 09
                    Daniel Krompass
                    X.P.

                    2. Fassung Berlin Sep 09
                    Daniel Krompass
                    Prototyp



                    Ich habe mehr Werkzeuge,
                    als meine Freundin Schuhe hat.


                    Feine Waffeln kommen aus meinem Waffeleisen
                    und Schokolade kaufe ich im KDW - that's berlin.

Ich bemühe mich um die Fertigstellung der 3ten, und damit endgültigen Fassung einschließlich des gesamten Codes zum Download im Zeitraum 21.12.09 - 24.12.09.

Alle Inhalte befinden sich bis zur 3ten und endgültigen Fassung noch in Arbeit. Im gleichen Zuge geben wir das Produkt für den Einsatz frei, und stellen den Quellcode zum Download bereit. Ich bedanke mich an dieser Stelle bei den Plattformbetreibern für das regelmäßige Update meiner Dokumentation. Aus dem Inhalt:

  • Einleitung zur Spezifikation, Zielgruppe, Hintergrund, Motivation und Ausblick.

  • In Teil I vermitteln wir die Anwendung an konkreten Beispielen.

  • In Teil II werfen wir einen Blick in die Implementierung der verschiedenen Verfahren.

  • In Teil III diskutieren wir Validierung und Verifikation.

Informelle Spezifikation

    Es ist nach einem kleinen aber flexiblem PHP-Programm zum Zeichnen von kartesischen Diagrammen gesucht. Wir folgen dabei einer intuitiven Vorgehensweise: Eine Zeichenebene dient als Container für unterschiedliche Diagrammtypen, so z.B. Linien-, Punkt- oder Kurvendiagramme. Jeder derartigen Komponente werden numerische Wertepaare übergeben, die dann über die jeweiligen Verfahren interpoliert oder approximiert werden. Wir wollen hier eine stückweise lineare Interpolation (Polygonzug), sowie eine natürliche Spline-Interpolation von Wertepaaren realisieren. Zu einem späteren Zeitpunkt ergänzen wir noch die Bestapproximation. Ferner wollen wir noch eine optionale Flächenfüllung zwischen der jeweiligen Kurve und der X-Achse ermöglichen.

Zielgruppe

    Jeder kann den PHP-Code privat wie auch kommerziell nutzen. Die Anwendung ist einfach und für jeden verständlich. Haftungsansprüche sind ausgeschlossen. Die Dokumentation darf hingegen weder vervielfältigt, verändert noch anderweitig genutzt werden. Für den Einsteiger können einige grundlegende Gedankengänge am konkreten Beispiel lehrreich sein. Bei ausreichender Resonanz oder konkreten Fragen kann die Dokumentation auch intensiver ausgearbeitet werden, so dass der Interessierte näher an die Problemlösung herangeführt werden kann. Die Umsetzung in PHP und das individuelle Erweiterungspotenzial der vorliegenden Lösung richtet sich hingegen an fortgeschrittene Programmierer mit teilweise erforderlichen Kenntnissen aus der numerischen Mathematik. Als praktisch orientierte Begleitlektüre kann Numerische Mathematik von Michael Knorrenschild herangezogen werden.

Hintergrund

    Bereits vorhandene Lösungen sind entweder fehlerhaft, zu kompliziert, oder haben nicht die gewünschte Leistung. Das folgende Programm zeigt, wie mit der zur Verfügung gestellten Syntax die gewünschte Anforderung umgesetzt werden kann, auch wenn die formalen Techniken alles andere als ideal sind. In der Tat zeigt dieses Beispiel nur geringfügig die Schwächen einer durchaus etablierten Sprache, die in ihrer Entwicklung Jahrzehnte zurückliegt. Programmumfang - keine 1000 Zeilen, Einarbeitung in die numerische Mathematik - ein Semester, Programmierdauer - wenige Stunden, Dokumentation - mehrere Stunden.

Motivation

    Seit dem Ende der Amigaprogrammierung (Gehirn-Jogging über Speicheradressen) wende ich i.A. keine herkömmliche Programmierung mehr an, denn die meisten Webapplikationen löse ich technologisch und übe mich gleichzeitig darin mit modernen Webframeworks zu konkurrieren. Ein Stück amüsante Programmiergeschichte kann man in diesen Werken nachlesen: http://www.birdys.de/default.php?page=buecher&kat=11&menu=1. Die Autoren Amir und Höfler sind maßgeblich für meine Entwicklung verantwortlich, die auf Grund gewisser Nebenwirkungen 1998 ihr vorläufiges Ende fand und mit der Popularität des Internets 2002 wieder aufgenommen wurde. Der erneute Versuch dem Enthusiasmus durch ein Lehramtsstudium zu entkommen scheiterte erbärmlich.
    Ein wissenschaftlicher Schachzug brächte eine Technologie hervor, die auf Grund des vorhandenen Potenzials alle bisherigen Techniken überholt. Ob der König am Leben bleibt, muss noch entschieden werden.

Ausblick

    Neben den hier vorgestellten Interpolationsverfahren spielt ebenso die Ausgleichsrechnung (Bestapproximation) eine praktische Rolle, deren Graphen im Unterschied dazu nicht zwangsläufig durch alle Wertepaare hindurchgehen. Vielmehr ist man an einer bestmöglichen (gewichteten) Annäherung durch einen Graphen mit vorgegebenen Eigenschaften interessiert. Je nach freien Kapazitäten komme ich darauf zurück.

Teil II
   Implementierungsdetails



                    Es ist schwierig zu antworten,
                    wenn man die Frage nicht kennt, aber nicht unmöglich.

Beim objektorientierten Entwurf muss man sich über zwei Dinge im klaren sein.

  1. Der Aufruf von Methoden ist i.A. nicht deterministisch, d.h. eine unbeabsichtigte, jedoch gültige Aufrufreihenfolge von Methoden kann zu unterschiedlichen oder gar falschen Ergebnissen führen. Möchte man eine deterministische Abfolge sicherstellen, dann produziert der objektorientierte Formalismus einen inakzeptablen Overhead.
  2. Auf Grund der überwiegend vorherrschenden Referenzsemantik kann ein System von Objekten mehrere Zugriffspunkte haben, an denen der Zustand des Gesamtsystems verändert werden kann. Optimierungen von gleichen Anweisungsfolgen mit trügerisch gleichen Ergebnissen sind selten durchführbar bzw. beherrschbar. Alle Hoffnungen stecken hier in der Compileroptimierung.

Die Klasse Graph

    Der Zustand einer Zeichenebene ist durch einige Eigenschaften charakterisiert. Die effektive Zeichenfläche ist durch ihre Breite $width und Höhe $height gegeben. Das Diagramm besitzt einen Koordinatenursprung $centerX und $centerY, der maßgeblich vom benötigten Platz der Achsenbeschriftungen abhängt. Die effektiven Abmessungen der Zeichenfläche leiten sich dann aus absoluter Gesamtgröße und Koordinatenursprung ab. Für die Zeichnung der Komponenten benötigen wir einen Grafikkontext $image.

     1 
     2    class Graph
     3    {
     4       public  $width;
     5       public  $height;
     6       public  $centerX;
     7       public  $centerY;
     8       public  $image;
     9       private $font;
    10       private $components;
    11       private $charHeight;
    12       private $charWidth;
    13 
    14       ...
    15    }
    16 


    Die internen Zustandseigenschaften werden über den Konstruktor festgelegt. Man kann sich überlegen diese über das Konstruktorargument zu parametrisieren. Die Abmessungen des Diagramms, sowie die zugehörige Hintergrund- und Achsenfarbe konkretisieren wir erst beim Zeichenvorgang Draw. Dadurch erreichen wir eine höhere Flexibilität.

    1 
    2    public function __construct()
    3    {
    4       $this->font       = 1;
    5       $this->components = array();
    6       $this->charWidth  = imagefontWidth($this->font);
    7       $this->charHeight = imagefontHeight($this->font);
    8    }
    9 


    Das Hinzufügen von Komponenten ist selbsterklärend.

    1 
    2    public function AddComponent(GraphComponent $c)
    3    {
    4       array_push($this->components, $c);
    5    }
    6 


    Eine wichtige Aufgabe erfüllen die folgenden Methoden. Sie geben den maximalen bzw. minimalen X-Wert und Y-Wert über allen (!) bereits gebundenen Komponenten zurück. Dabei delegieren wir die Berechnung an alle Komponenten weiter. Wir brauchen diese Werte um die Skalierung jeder einzelnen Komponente berechnen zu können, denn schließlich muss jede Komponente in dem Diagramm auch «Platz» finden. Wir erhalten letztlich ein Diagramm, in dem gilt:

    • Der kleinste X-Wert über allen Komponenten liegt stets auf der Y-Achse.
    • Der größte X-Wert über allen Komponenten liegt stets am rechten äußersten Rand.
    • Der kleinste Y-Wert über allen Komponenten liegt stets auf der X-Achse.
    • Der größte Y-Wert über allen Komponenten liegt stets am obersten Rand.

    Man kann sich diese Eigenschaften erneut am folgenden Beispiel vor Augen führen.



     1 
     2    public function GetMaxX()
     3    {
     4       $maxX = $this->components[0]->GetMaxX();
     5       foreach ($this->components as $c)
     6       $maxX = max($maxX, $c->GetMaxX());
     7       return $maxX;
     8    }
     9    public function GetMinX()
    10    {
    11       $minX = $this->components[0]->GetMinX();
    12       foreach ($this->components as $c)
    13       $minX = min($minX, $c->GetMinX());
    14       return $minX;
    15    }
    16    public function GetMaxY()
    17    {
    18       $maxY = $this->components[0]->GetMaxY();
    19       foreach ($this->components as $c)
    20       $maxY = max($maxY, $c->GetMaxY());
    21       return $maxY;
    22    }
    23    public function GetMinY()
    24    {
    25       $minY = $this->components[0]->GetMinY();
    26       foreach ($this->components as $c)
    27       $minY = min($minY, $c->GetMinY());
    28       return $minY;
    29    }
    30 


    Offensichtlich setzen wir voraus, dass es mindestens eine Komponente geben muss. Andernfalls kommt es zu einem Laufzeitfehler, den wir nicht weiter behandeln wollen, da das Programm in diesem Falle ohnehin nicht sinnvoll fortgesetzt werden kann. Hinzu kommt, dass diese Methoden nicht für den Benutzer bestimmt sind. Sie dienen ausschließlich dem Programm. Zum Schluss definieren wir noch die eigentliche Zeichenmethode Draw.

    1 
    2    public function Draw($fgcolor, $bgcolor, $xDim, $yDim, $stepsX, $stepsY,
    3                         $mapX="", $mapY="", $pX=0, $pY=0)
    4    {
    5       $im = imagecreate($xDim, $yDim);
    6 
    7       ...
    8    }
    9 


    Sie gehört zu den typischen Kandidaten in der täglichen Programmierarbeit, die als lästig oder nervtötend, jedoch weniger als schwierig oder kompliziert empfunden werden. Solche Burschen verbergen in sich eine gewisse Esoterik und gehören der «Fraktion karierter Hemden» an. Die dezimalen RGB-Farbwerte extrahieren wir aus dem hexadezimalen Wert, indem wir stückweise die zweistelligen Farbwerte abtrennen:

     1 
     2    $b       = $bgcolor % 0x100;
     3    $bgcolor = $bgcolor >> 8;
     4    $g       = $bgcolor % 0x100;
     5    $r       = $bgcolor >> 8;
     6    $bgcolor = imagecolorallocate($im, $r, $g, $b);
     7 
     8    $b       = $fgcolor % 0x100;
     9    $fgcolor = $fgcolor >> 8;
    10    $g       = $fgcolor % 0x100;
    11    $r       = $fgcolor >> 8;
    12    $fgcolor = imagecolorallocate($im, $r, $g, $b);
    13 


    Zum Verständnis ein Beispiel aus dem Dezimalsystem. Wir wollen von 1024 die 4 abtrennen, indem wir den Divisionsrest durch die Anweisung 1024 % 10 (ergibt 4) bestimmen. Die Ganzzahldivision (int)(1024 / 10) ergibt 102. Wir fahren analog fort.

    Die maximalen und minimalen Intervallgrenzen auf der X- bzw. Y-Achse sind gegeben durch:

    1 
    2    $minY = $this->GetMinY();
    3    $maxY = $this->GetMaxY();
    4    $minX = $this->GetMinX();
    5    $maxX = $this->GetMaxX();
    6 


    Mit anderen Worten reicht die Beschriftung der X-Achse von $minX bis $maxX. Ebenso auf der Y-Achse von $minY bis $maxY. Zum Verständnis der nachfolgenden Berechnung wollen wir uns zuerst ein Beispiel erneut vor Augen führen.



    Die richtige Idee an der richtigen Stelle ist maßgeblich für die Qualität des Programms verantwortlich. Wir berechnen folgend den Koordinatenursprung, sowie die effektiven Abmessungen der Zeichenebene. Die Existenz solcher Werte bildet für uns eine solide Grundlage für den Zeichenvorgang aller bisher entwickelten Komponenten.

    Der folgende Zeichenvorgang kann nur mit gültigen Intervallen fortgesetzt werden.

    1 
    2    if ($maxY - $minY && $maxX - $minX)
    3    {
    4       ...
    5    }
    6    return $im;
    7 


    Zum Zeichenvorgang gehört zunächst die Berechnung des Koordinatenursprungs $cx, $cy. An den Intervallgrenzen $minX, $maxX, $minY und $maxY rechnen wir mit den exakten Werten. Der horizontale Platzanspruch zur Beschriftung der Y-Achse errechnet sich über:

     1 
     2    $cw = $this->charWidth;
     3    # Wenn eine Map-Funktion gegeben ist.
     4    if ($mapY)
     5    {
     6       # Obere Grenze
     7       $num = $maxY;
     8       $val = call_user_func($mapY, $num);
     9       $cx = $cw * strlen($val);
    10       $step = ($maxY - $minY) / $stepsY;
    11 
    12       # Zwischenwerte
    13       for ($i = 1; $i < $stepsY; $i++)
    14       {
    15          $num = $num - $i * $step;
    16          $val = call_user_func($mapY, $num);
    17          $cx = max($cx, $cw * strlen($val));
    18       }
    19 
    20       # Untere Grenze
    21       $num = $minY;
    22       $val = call_user_func($mapY, $num);
    23       $cx = max($cx, $cw * strlen($val));
    24    }
    25 
    26    # Wenn keine Map-Funktion gegeben ist.
    27    else
    28    {
    29       # Obere Grenze
    30       $num = $maxY;
    31       $cx = $cw * strlen($num);
    32       $step = ($maxY - $minY) / $stepsY;
    33 
    34       # Zwischenwerte
    35       for ($i = 1; $i < $stepsY; $i++)
    36       {
    37          $num = $num - $i * $step;
    38          $cx = max($cx, $cw * strlen($num));
    39       }
    40 
    41       # Untere Grenze
    42       $num = $minY;
    43       $cx = max($cx, $cw * strlen($num));
    44    }
    45 


    Doch damit nicht genug, denn auch der erste Wert auf der X-Achse könnte über alle Grenzen hinauswachsen. Gegebenfalls ist also eine Korrektur vorzunehmen.

    1 
    2    if ($mapX)
    3    {
    4       $val = call_user_func($mapX, $minX);
    5       $cx = max($cx, $cw * strlen($val) / 2);
    6    }
    7    else $cx = max($cx, $cw * strlen($minX) / 2);
    8 


    Folgewerte interessieren uns nicht, da diese ohnehin ihren Vorgänger überdecken würden, was niemand gebrauchen kann. Wir rücken noch ein wenig ein.

    1 
    2    $cx += 10;
    3 


    Der vertikale Platzanspruch soll ein fester Wert sein.

    1 
    2    $cy = $yDim - 25;
    3 


    Das Spektakel setzen wir nun mit der Berechnung der effektiven Größe der Zeichenfläche fort.

     1 
     2    # Wir halten oben einen Abstand von 5 Pixel ein,
     3    $h = $cy - 5;
     4 
     5    # und ermitteln den rechten Abstand über
     6    if ($mapX)
     7    {
     8       $val = call_user_func($mapX, $maxX);
     9       $w = $xDim - $cx - $cw * strlen($val) / 2 - 5;
    10    }
    11    else
    12    $w = $xDim - $cx - $cw * strlen($maxX) / 2 - 5;
    13 


    Da wir jetzt den Koordinatenursprung und die effektiven Abmessungen der Zeichenfläche kennen, können wir die Koordinatenachsen zeichnen.

    1 
    2    imageline($im, $cx, $cy, $cx + $w, $cy, $fgcolor);
    3    imageline($im, $cx, $cy, $cx, $cy - $h, $fgcolor);
    4 
    5    # Stauchung $pY und effektive Zeichenhöhe miteinbeziehen.
    6    $cy -= $pY;
    7    $h  -= 2 * $pY;
    8 


    Was noch fehlt, sind die Intervallstriche, sowie die entsprechenden Beschriftungen. Zunächst legen wir fest:

    1 
    2    $f  = $this->font;
    3    $ch = $this->charHeight / 2;
    4 


    Wir beginnen mit der Y-Achse,

     1 
     2    $a = $h / $stepsY;
     3    $b = ($maxY - $minY) / $stepsY;
     4    for ($i = 0; $i <= $stepsY; $i++)
     5    {
     6       if ($i < $stepsY)
     7       {
     8          $x1  = $cx - 5;
     9          $y1  = $cy - $i * $a;
    10          $x2  = $cx;
    11          $y2  = $y1;
    12          $num = $minY + $i * $b;
    13       }
    14 
    15       # Für den letzten Schritt nehmen wir den exakten Wert.
    16       else
    17       {
    18          $x1  = $cx - 5;
    19          $y1  = $cy - $h;
    20          $x2  = $cx;
    21          $y2  = $y1;
    22          $num = $maxY;
    23       }
    24 
    25       # Intervallstrich
    26       imageline($im, $x1, $y1, $x2, $y2, $fgcolor);
    27 
    28       # Beschriftung
    29       if ($mapY) $num = call_user_func($mapY, $num);
    30       $m = $cw * strlen($num) + 2;
    31       imagestring($im, $f, $x1 - $m, $y1 - $ch, $num, $fgcolor);
    32    }
    33 


    und setzen auf der X-Achse analog fort. Dabei machen wir zuerst die Y-Stauchung kurzfristig rückgängig, sonst würde nämlich unsere Achsenbeschriftung verrutschen. Dafür beziehen wir jetzt die horizontale Stauchung $pX und die effektive Breite mit ein.

     1 
     2    $cy += $pY;
     3    $cx += $pX;
     4    $w  -= 2 * $pX;
     5    $a = $w / $stepsX;
     6    $b = ($maxX - $minX) / $stepsX;
     7    for ($i = 0; $i <= $stepsX; $i++)
     8    {
     9       # Berechnung des ersten und aller folgenden Zwischenschritte.
    10       if ($i < $stepsX)
    11       {
    12          $x1  = $cx + $i * $a;
    13          $y1  = $cy;
    14          $x2  = $x1;
    15          $y2  = $cy + 5;
    16          $num = $minX + $i * $b;
    17       }
    18 
    19       # Für den letzten Schritt nehmen wir den exakten Wert.
    20       else
    21       {
    22          $x1  = $cx + $w;
    23          $y1  = $cy;
    24          $x2  = $x1;
    25          $y2  = $cy + 5;
    26          $num = $maxX;
    27       }
    28 
    29       # Intervallstrich
    30       imageline($im, $x1, $y1, $x2, $y2, $fgcolor);
    31 
    32       # Beschriftung
    33       if ($mapX) $num = call_user_func($mapX, $num);
    34       $m = $cw * strlen($num) / 2;
    35       imagestring($im, $f, $x1 - $m, $y2 + 2, $num, $fgcolor);
    36    }
    37 


    Zum Abschluss delegieren wir den Zeichenvorgang noch an alle Komponenten weiter. Man beachte, die vertikale Stauchung wiederherzustellen.

    1 
    2    $this->image   = $im;
    3    $this->centerX = $cx;
    4    $this->centerY = $cy - $pY;
    5    $this->width   = $w;
    6    $this->height  = $h;
    7    foreach ($this->components as $c)
    8    $c->Draw($this);
    9 


    Der geübte Programmierer kann den obigen Zeichenvorgang Draw ohne Mühe auf individuelle Bedürfnisse anpassen. So z.B. ein übliches Diagrammgitter oder eine andere Darstellung der Achsenbeschriftungen. Eine Legende erfordert hingegen etwas Geschick.

    Diese Klasse definiert noch eine kleine Hilfsfunktion zum Zeichnen von Verläufen. Sie hat nichts mit dem eigentlichen Typ Graph zu tun.

     1 
     2    public static function Gradient($r1, $g1, $b1, $r2, $g2, $b2, $width, $im)
     3    {
     4       $verlauf = array();
     5       if ($width)
     6       {
     7          $offset1 = (($r2 - $r1) / $width);
     8          $offset2 = (($g2 - $g1) / $width);
     9          $offset3 = (($b2 - $b1) / $width);
    10          for ($i = 0; $i < $width - 1; $i++)
    11          {
    12             $rcol = $r1 + $offset1 * $i;
    13             $gcol = $g1 + $offset2 * $i;
    14             $bcol = $b1 + $offset3 * $i;
    15             $verlauf[$i] = imagecolorallocate($im, $rcol, $gcol, $bcol);
    16          }
    17          $verlauf[$width-1] = imagecolorallocate($im, $r2, $g2, $b2);
    18       }
    19       return $verlauf;
    20    }
    21 


Die Schnittstelle GraphComponent

    Die Schnittstelle GraphComponent gibt spezifische Eigenschaften vor, die jede Komponente erfüllen muss, die Graph fordert.

     1 
     2    interface GraphComponent
     3    {
     4       public function Draw(Graph $g);
     5       public function GetMaxX();
     6       public function GetMinX();
     7       public function GetMaxY();
     8       public function GetMinY();
     9    }
    10 


    Die häufig verwendete Methode AddPoint gehört nicht dazu.

Die Klasse GraphPoints

    Die Klasse GraphPoints ist leicht zu implementieren und stellt die übergebenen Wertepaare als quadratische «Punkte» dar. Sie muss die Eigenschaften von GraphComponent erfüllen. Die meisten Komponenten besitzen auch eine Basis, die die Werte der minimalen X- und Y-Werte maßgeblich beeinflusst, wie wir gleich noch sehen werden.

    1 
    2    class GraphPoints implements GraphComponent
    3    {
    4       ...
    5    }
    6 


    Die internen Eigenschaften sind durch die Menge aller Wertepaare, sowie durch einen RGB-Farbwert gegeben.

    1 
    2    private $points = array();
    3    private $red    = 0;
    4    private $green  = 0;
    5    private $blue   = 0;
    6    private $baseX  = 0;
    7    private $baseY  = 0;
    8 


    Der Konstruktor nimmt einen solchen RGB-Farbwert, sowie eine optionale Basis entgegen.

     1 
     2    public function __construct($col, $bX=0, $bY=0)
     3    {
     4       $this->blue  = $col % 0x100;
     5       $col         = $col >> 8;
     6       $this->green = $col % 0x100;
     7       $this->red   = $col >> 8;
     8       $this->baseX = $bX;
     9       $this->baseY = $bY;
    10    }
    11 


    Das Hinzufügen von Wertepaaren ist selbsterklärend. Die Punkte werden gemäß der Einfügereihenfolge gezeichnet.

    1 
    2    public function AddPoint($xPos, $yPos)
    3    {
    4       array_push($this->points, array($xPos, $yPos));
    5    }
    6 


    Die folgenden Methoden ermittel den maximalen bzw. minimalen X- und Y-Wert dieser Komponente.

     1 
     2    public function GetMaxX()
     3    {
     4       $maxX = $this->points[0][0];
     5       foreach ($this->points as $point)
     6       $maxX = max($maxX, $point[0]);
     7       return $maxX;
     8    }
     9    public function GetMinX()
    10    {
    11       $minX = $this->points[0][0] - $this->baseX;
    12       foreach ($this->points as $point)
    13       $minX = min($minX, $point[0] - $this->baseX);
    14       return $minX;
    15    }
    16    public function GetMaxY()
    17    {
    18       $maxY = $this->points[0][1];
    19       foreach ($this->points as $point)
    20       $maxY = max($maxY, $point[1]);
    21       return $maxY;
    22    }
    23    public function GetMinY()
    24    {
    25       $minY = $this->points[0][1] - $this->baseY;
    26       foreach ($this->points as $point)
    27       $minY = min($minY, $point[1] - $this->baseY);
    28       return $minY;
    29    }
    30 


    Auch hier gilt wieder, dass wir offensichtlich wenigstens einen Punkt voraussetzen. Wie üblich ist es wieder die Methode Draw, die ein wenig mehr Erklärung bedarf.

    1 
    2    public function Draw(Graph $g)
    3    {
    4       ...
    5    }
    6 


    Wir ermitteln zunächst wieder die Ober- und Untergrenze der Intervalle für die Skalierung und legen einige Variablen fest.

     1 
     2    $im    = $g->image;
     3    $c     = imagecolorallocate($im, $this->red, $this->green, $this->blue);
     4    $maxX  = $g->GetMaxX();
     5    $minX  = $g->GetMinX();
     6    $maxY  = $g->GetMaxY();
     7    $minY  = $g->GetMinY();
     8    $pt    = $this->points;
     9    $w     = $g->width;
    10    $h     = $g->height;
    11    $cx    = $g->centerX;
    12    $cy    = $g->centerY;
    13 


    Der Zeichenvorgang skaliert jeden Punkt auf die richtigen Werte, und errechnet daraus dann die Koordinaten für das «Quadrat», dessen Seitenlänge 4 Pixel sein soll.

     1 
     2    for ($i = 0; $i < sizeOf($pt); $i++)
     3    {
     4       # Koordinaten skalieren und Koordinaten für das "Quadrat" bestimmen.
     5       $x1 = $w * ($pt[$i][0] - $minX) / ($maxX - $minX) - 2;
     6       $y1 = $h * ($pt[$i][1] - $minY) / ($maxY - $minY) - 2;
     7       $x2 = $x1 + 4;
     8       $y2 = $y1 + 4;
     9 
    10       # Quadrat zeichnen.
    11       imagerectangle($im, $cx + $x1, $cy - $y1, $cx + $x2, $cy - $y2, $c);
    12    }
    13 


Die Klasse GraphLinear

    Die lineare Interpolation ist nicht schwieriger als die punktweise Interpolation. Die Punkte werden gemäß der Einfügereihenfolge über Linien miteinander verbunden. Wir betrachten erneut ein Beispiel.




    1 
    2    class GraphLinear implements GraphComponent
    3    {
    4       ...
    5    }
    6 


    Die Klasse GraphLinear unterscheidet sich von der Klasse GraphPoints nur im Zeichenvorgang Draw, den wir uns genauer ansehen wollen.

    1 
    2    public function Draw(Graph $g)
    3    {
    4       ...
    5    }
    6 


    Wie üblich ermitteln wir zunächst wieder die Ober- und Untergrenze der Intervalle für die Skalierung und legen einige Variablen fest.

     1 
     2    $im    = $g->image;
     3    $c     = imagecolorallocate($im, $this->red, $this->green, $this->blue);
     4    $maxX  = $g->GetMaxX();
     5    $minX  = $g->GetMinX();
     6    $maxY  = $g->GetMaxY();
     7    $minY  = $g->GetMinY();
     8    $pt    = $this->points;
     9    $w     = $g->width;
    10    $h     = $g->height;
    11    $cx    = $g->centerX;
    12    $cy    = $g->centerY;
    13 


    Als naheliegende Lösung bietet sich in Analogie zur Zeichenmethode GraphPoints.Draw folgender Ansatz an.

     1 
     2    for ($i = 1; $i < sizeOf($pt); $i++)
     3    {
     4       # Koordinaten der Linieneckpunkte auslesen.
     5       $x1 = $pt[$i][0];
     6       $y1 = $pt[$i][1];
     7       $x2 = $pt[$i-1][0];
     8       $y2 = $pt[$i-1][1];
     9 
    10       # Koordinaten skalieren.
    11       $x1 = $w * ($x1 - $minX) / ($maxX - $minX);
    12       $y1 = $h * ($y1 - $minY) / ($maxY - $minY);
    13       $x2 = $w * ($x2 - $minX) / ($maxX - $minX);
    14       $y2 = $h * ($y2 - $minY) / ($maxY - $minY);
    15 
    16       imageline($im, $cx + $x1, $cy - $y1, $cx + $x2, $cy - $y2, $c);
    17    }
    18 


    Dies ist ein Beispiel, das wir oft in ähnlicher Form vorfinden. Die Iteration eines Arrays greift zusätzlich auf seinen unmittelbaren Vorgänger oder Nachfolger zu. Man kann den Zugriff auf Arrayelemente durch folgenden Ansatz vermindern.

     1 
     2    $x1 = $w * ($pt[0][0] - $minX) / ($maxX - $minX);
     3    $y1 = $h * ($pt[0][1] - $minY) / ($maxY - $minY);
     4    for ($i = 1; $i < sizeOf($pt); $i++)
     5    {
     6       $x2 = $w * ($pt[$i][0] - $minX) / ($maxX - $minX);
     7       $y2 = $h * ($pt[$i][1] - $minY) / ($maxY - $minY);
     8       imageline($im, $cx + $x1, $cy - $y1, $cx + $x2, $cy - $y2, $c);
     9       $x1 = $x2;
    10       $y1 = $y2;
    11    }
    12 


    Wir setzen auch hier wieder voraus, dass es mindestens einen Punkt geben muss (im Unterschied zum obigen Ansatz!), aber der Laufzeitfehler tritt ohnehin schon bei der Bestimmung von MaxXY und MinXY auf. Gezeichnet wird erst bei mindestens zwei Punkten.

Die Klasse GraphBar



 

Rate the tutorial

Did you like this tutorial? Than rate it now! Five stars means "very good", one star "very bad".

About the author
EVAMasters

EVAMasters

State
Premium Member

Occupation
Angestellter

Member since:
2009.07.21

Last activity
2010.03.04

 

 

aktuelle Artikel

PHP 5.3.2 60 Fehler weniger

PHP 5.3.2 60 Fehler wenigerDie Core-Developer von PHP haben ein weiteres Maintenance-Release von PHP 5.3 veröffentlicht. Neben dem schließen von Sicherheitslücken wurden 60 bekannte Fehler in der aktuellen PHP 5.3.2 Version behoben.

05.03.2010 | Neu | Berni

500. Artikel bei phphatesme.com

500. Artikel bei phphatesme.comDer 500. Artikel ist veröffentlicht. Eine Leistung, die den jungen Blog direkt an die Spitze der deutschsprachigen Bloggerszene katapultiert hat und das innnerhalb der letzten 1,5 Jahren.

22.02.2010 | Neu | phphatesme