source: trunk/xgraph/jpgraph/jpgraph_plotband.php @ 42

Last change on this file since 42 was 42, checked in by marrucho, 10 years ago
File size: 21.2 KB
Line 
1<?php
2//=======================================================================
3// File:        JPGRAPH_PLOTBAND.PHP
4// Description: PHP4 Graph Plotting library. Extension module.
5// Created:     2004-02-18
6// Ver:         $Id: jpgraph_plotband.php 1106 2009-02-22 20:16:35Z ljp $
7//
8// Copyright (c) Asial Corporation. All rights reserved.
9//========================================================================
10
11// Constants for types of static bands in plot area
12define("BAND_RDIAG",1); // Right diagonal lines
13define("BAND_LDIAG",2); // Left diagonal lines
14define("BAND_SOLID",3); // Solid one color
15define("BAND_VLINE",4); // Vertical lines
16define("BAND_HLINE",5);  // Horizontal lines
17define("BAND_3DPLANE",6);  // "3D" Plane
18define("BAND_HVCROSS",7);  // Vertical/Hor crosses
19define("BAND_DIAGCROSS",8); // Diagonal crosses
20
21
22// Utility class to hold coordinates for a rectangle
23class Rectangle {
24    public $x,$y,$w,$h;
25    public $xe, $ye;
26    function __construct($aX,$aY,$aWidth,$aHeight) {
27        $this->x=$aX;
28        $this->y=$aY;
29        $this->w=$aWidth;
30        $this->h=$aHeight;
31        $this->xe=$aX+$aWidth-1;
32        $this->ye=$aY+$aHeight-1;
33    }
34}
35
36//=====================================================================
37// Class RectPattern
38// Base class for pattern hierarchi that is used to display patterned
39// bands on the graph. Any subclass that doesn't override Stroke()
40// must at least implement method DoPattern($aImg) which is responsible
41// for drawing the pattern onto the graph.
42//=====================================================================
43class RectPattern {
44    protected $color;
45    protected $weight;
46    protected $rect=null;
47    protected $doframe=true;
48    protected $linespacing; // Line spacing in pixels
49    protected $iBackgroundColor=-1;  // Default is no background fill
50
51    function __construct($aColor,$aWeight=1) {
52        $this->color = $aColor;
53        $this->weight = $aWeight;
54    }
55
56    function SetBackground($aBackgroundColor) {
57        $this->iBackgroundColor=$aBackgroundColor;
58    }
59
60    function SetPos($aRect) {
61        $this->rect = $aRect;
62    }
63
64    function ShowFrame($aShow=true) {
65        $this->doframe=$aShow;
66    }
67
68    function SetDensity($aDens) {
69        if( $aDens < 1 || $aDens > 100 )
70        JpGraphError::RaiseL(16001,$aDens);
71        //(" Desity for pattern must be between 1 and 100. (You tried $aDens)");
72        // 1% corresponds to linespacing=50
73        // 100 % corresponds to linespacing 1
74        $this->linespacing = floor(((100-$aDens)/100.0)*50)+1;
75
76    }
77
78    function Stroke($aImg) {
79        if( $this->rect == null )
80        JpGraphError::RaiseL(16002);
81        //(" No positions specified for pattern.");
82
83        if( !(is_numeric($this->iBackgroundColor) && $this->iBackgroundColor==-1) ) {
84            $aImg->SetColor($this->iBackgroundColor);
85            $aImg->FilledRectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
86        }
87
88        $aImg->SetColor($this->color);
89        $aImg->SetLineWeight($this->weight);
90
91        // Virtual function implemented by subclass
92        $this->DoPattern($aImg);
93
94        // Frame around the pattern area
95        if( $this->doframe )
96        $aImg->Rectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
97    }
98
99}
100
101
102//=====================================================================
103// Class RectPatternSolid
104// Implements a solid band
105//=====================================================================
106class RectPatternSolid extends RectPattern {
107
108    function __construct($aColor="black",$aWeight=1) {
109        parent::__construct($aColor,$aWeight);
110    }
111
112    function DoPattern($aImg) {
113        $aImg->SetColor($this->color);
114        $aImg->FilledRectangle($this->rect->x,$this->rect->y,
115        $this->rect->xe,$this->rect->ye);
116    }
117}
118
119//=====================================================================
120// Class RectPatternHor
121// Implements horizontal line pattern
122//=====================================================================
123class RectPatternHor extends RectPattern {
124
125    function __construct($aColor="black",$aWeight=1,$aLineSpacing=7) {
126        parent::__construct($aColor,$aWeight);
127        $this->linespacing = $aLineSpacing;
128    }
129
130    function DoPattern($aImg) {
131        $x0 = $this->rect->x;
132        $x1 = $this->rect->xe;
133        $y = $this->rect->y;
134        while( $y < $this->rect->ye ) {
135            $aImg->Line($x0,$y,$x1,$y);
136            $y += $this->linespacing;
137        }
138    }
139}
140
141//=====================================================================
142// Class RectPatternVert
143// Implements vertical line pattern
144//=====================================================================
145class RectPatternVert extends RectPattern {
146
147    function __construct($aColor="black",$aWeight=1,$aLineSpacing=7) {
148        parent::__construct($aColor,$aWeight);
149        $this->linespacing = $aLineSpacing;
150    }
151
152    //--------------------
153    // Private methods
154    //
155    function DoPattern($aImg) {
156        $x = $this->rect->x;
157        $y0 = $this->rect->y;
158        $y1 = $this->rect->ye;
159        while( $x < $this->rect->xe ) {
160            $aImg->Line($x,$y0,$x,$y1);
161            $x += $this->linespacing;
162        }
163    }
164}
165
166
167//=====================================================================
168// Class RectPatternRDiag
169// Implements right diagonal pattern
170//=====================================================================
171class RectPatternRDiag extends RectPattern {
172
173    function __construct($aColor="black",$aWeight=1,$aLineSpacing=12) {
174        parent::__construct($aColor,$aWeight);
175        $this->linespacing = $aLineSpacing;
176    }
177
178    function DoPattern($aImg) {
179        //  --------------------
180        //  | /   /   /   /   /|
181        //  |/   /   /   /   / |
182        //  |   /   /   /   /  |
183        //  --------------------
184        $xe = $this->rect->xe;
185        $ye = $this->rect->ye;
186        $x0 = $this->rect->x + round($this->linespacing/2);
187        $y0 = $this->rect->y;
188        $x1 = $this->rect->x;
189        $y1 = $this->rect->y + round($this->linespacing/2);
190
191        while($x0<=$xe && $y1<=$ye) {
192            $aImg->Line($x0,$y0,$x1,$y1);
193            $x0 += $this->linespacing;
194            $y1 += $this->linespacing;
195        }
196
197        if( $xe-$x1 > $ye-$y0 ) {
198            // Width larger than height
199            $x1 = $this->rect->x + ($y1-$ye);
200            $y1 = $ye;
201            $y0 = $this->rect->y;
202            while( $x0 <= $xe ) {
203                $aImg->Line($x0,$y0,$x1,$y1);
204                $x0 += $this->linespacing;
205                $x1 += $this->linespacing;
206            }
207             
208            $y0=$this->rect->y + ($x0-$xe);
209            $x0=$xe;
210        }
211        else {
212            // Height larger than width
213            $diff = $x0-$xe;
214            $y0 = $diff+$this->rect->y;
215            $x0 = $xe;
216            $x1 = $this->rect->x;
217            while( $y1 <= $ye ) {
218                $aImg->Line($x0,$y0,$x1,$y1);
219                $y1 += $this->linespacing;
220                $y0 += $this->linespacing;
221            }
222             
223            $diff = $y1-$ye;
224            $y1 = $ye;
225            $x1 = $diff + $this->rect->x;
226        }
227
228        while( $y0 <= $ye ) {
229            $aImg->Line($x0,$y0,$x1,$y1);
230            $y0 += $this->linespacing;
231            $x1 += $this->linespacing;
232        }
233    }
234}
235
236//=====================================================================
237// Class RectPatternLDiag
238// Implements left diagonal pattern
239//=====================================================================
240class RectPatternLDiag extends RectPattern {
241
242    function __construct($aColor="black",$aWeight=1,$aLineSpacing=12) {
243        $this->linespacing = $aLineSpacing;
244        parent::__construct($aColor,$aWeight);
245    }
246
247    function DoPattern($aImg) {
248        //  --------------------
249        //  |\   \   \   \   \ |
250        //  | \   \   \   \   \|
251        //  |  \   \   \   \   |
252        //  |------------------|
253        $xe = $this->rect->xe;
254        $ye = $this->rect->ye;
255        $x0 = $this->rect->x + round($this->linespacing/2);
256        $y0 = $this->rect->ye;
257        $x1 = $this->rect->x;
258        $y1 = $this->rect->ye - round($this->linespacing/2);
259
260        while($x0<=$xe && $y1>=$this->rect->y) {
261            $aImg->Line($x0,$y0,$x1,$y1);
262            $x0 += $this->linespacing;
263            $y1 -= $this->linespacing;
264        }
265        if( $xe-$x1 > $ye-$this->rect->y ) {
266            // Width larger than height
267            $x1 = $this->rect->x + ($this->rect->y-$y1);
268            $y0=$ye; $y1=$this->rect->y;
269            while( $x0 <= $xe ) {
270                $aImg->Line($x0,$y0,$x1,$y1);
271                $x0 += $this->linespacing;
272                $x1 += $this->linespacing;
273            }
274             
275            $y0=$this->rect->ye - ($x0-$xe);
276            $x0=$xe;
277        }
278        else {
279            // Height larger than width
280            $diff = $x0-$xe;
281            $y0 = $ye-$diff;
282            $x0 = $xe;
283            while( $y1 >= $this->rect->y ) {
284                $aImg->Line($x0,$y0,$x1,$y1);
285                $y0 -= $this->linespacing;
286                $y1 -= $this->linespacing;
287            }
288            $diff = $this->rect->y - $y1;
289            $x1 = $this->rect->x + $diff;
290            $y1 = $this->rect->y;
291        }
292        while( $y0 >= $this->rect->y ) {
293            $aImg->Line($x0,$y0,$x1,$y1);
294            $y0 -= $this->linespacing;
295            $x1 += $this->linespacing;
296        }
297    }
298}
299
300//=====================================================================
301// Class RectPattern3DPlane
302// Implements "3D" plane pattern
303//=====================================================================
304class RectPattern3DPlane extends RectPattern {
305    private $alpha=50;  // Parameter that specifies the distance
306    // to "simulated" horizon in pixel from the
307    // top of the band. Specifies how fast the lines
308    // converge.
309
310    function __construct($aColor="black",$aWeight=1) {
311        parent::__construct($aColor,$aWeight);
312        $this->SetDensity(10);  // Slightly larger default
313    }
314
315    function SetHorizon($aHorizon) {
316        $this->alpha=$aHorizon;
317    }
318
319    function DoPattern($aImg) {
320        // "Fake" a nice 3D grid-effect.
321        $x0 = $this->rect->x + $this->rect->w/2;
322        $y0 = $this->rect->y;
323        $x1 = $x0;
324        $y1 = $this->rect->ye;
325        $x0_right = $x0;
326        $x1_right = $x1;
327
328        // BTW "apa" means monkey in Swedish but is really a shortform for
329        // "alpha+a" which was the labels I used on paper when I derived the
330        // geometric to get the 3D perspective right.
331        // $apa is the height of the bounding rectangle plus the distance to the
332        // artifical horizon (alpha)
333        $apa = $this->rect->h + $this->alpha;
334
335        // Three cases and three loops
336        // 1) The endpoint of the line ends on the bottom line
337        // 2) The endpoint ends on the side
338        // 3) Horizontal lines
339
340        // Endpoint falls on bottom line
341        $middle=$this->rect->x + $this->rect->w/2;
342        $dist=$this->linespacing;
343        $factor=$this->alpha /($apa);
344        while($x1>$this->rect->x) {
345            $aImg->Line($x0,$y0,$x1,$y1);
346            $aImg->Line($x0_right,$y0,$x1_right,$y1);
347            $x1 = $middle - $dist;
348            $x0 = $middle - $dist * $factor;
349            $x1_right = $middle + $dist;
350            $x0_right =  $middle + $dist * $factor;
351            $dist += $this->linespacing;
352        }
353
354        // Endpoint falls on sides
355        $dist -= $this->linespacing;
356        $d=$this->rect->w/2;
357        $c = $apa - $d*$apa/$dist;
358        while( $x0>$this->rect->x ) {
359            $aImg->Line($x0,$y0,$this->rect->x,$this->rect->ye-$c);
360            $aImg->Line($x0_right,$y0,$this->rect->xe,$this->rect->ye-$c);
361            $dist += $this->linespacing;
362            $x0 = $middle - $dist * $factor;
363            $x1 = $middle - $dist;
364            $x0_right =  $middle + $dist * $factor;
365            $c = $apa - $d*$apa/$dist;
366        }
367
368        // Horizontal lines
369        // They need some serious consideration since they are a function
370        // of perspective depth (alpha) and density (linespacing)
371        $x0=$this->rect->x;
372        $x1=$this->rect->xe;
373        $y=$this->rect->ye;
374
375        // The first line is drawn directly. Makes the loop below slightly
376        // more readable.
377        $aImg->Line($x0,$y,$x1,$y);
378        $hls = $this->linespacing;
379
380        // A correction factor for vertical "brick" line spacing to account for
381        // a) the difference in number of pixels hor vs vert
382        // b) visual apperance to make the first layer of "bricks" look more
383        // square.
384        $vls = $this->linespacing*0.6;
385
386        $ds = $hls*($apa-$vls)/$apa;
387        // Get the slope for the "perspective line" going from bottom right
388        // corner to top left corner of the "first" brick.
389
390        // Uncomment the following lines if you want to get a visual understanding
391        // of what this helpline does. BTW this mimics the way you would get the
392        // perspective right when drawing on paper.
393        /*
394        $x0 = $middle;
395        $y0 = $this->rect->ye;
396        $len=floor(($this->rect->ye-$this->rect->y)/$vls);
397        $x1 = $middle+round($len*$ds);
398        $y1 = $this->rect->ye-$len*$vls;
399        $aImg->PushColor("red");
400        $aImg->Line($x0,$y0,$x1,$y1);
401        $aImg->PopColor();
402        */
403
404        $y -= $vls;
405        $k=($this->rect->ye-($this->rect->ye-$vls))/($middle-($middle-$ds));
406        $dist = $hls;
407        while( $y>$this->rect->y ) {
408            $aImg->Line($this->rect->x,$y,$this->rect->xe,$y);
409            $adj = $k*$dist/(1+$dist*$k/$apa);
410            if( $adj < 2 ) $adj=1;
411            $y = $this->rect->ye - round($adj);
412            $dist += $hls;
413        }
414    }
415}
416
417//=====================================================================
418// Class RectPatternCross
419// Vert/Hor crosses
420//=====================================================================
421class RectPatternCross extends RectPattern {
422    private $vert=null;
423    private $hor=null;
424    function __construct($aColor="black",$aWeight=1) {
425        parent::__construct($aColor,$aWeight);
426        $this->vert = new RectPatternVert($aColor,$aWeight);
427        $this->hor  = new RectPatternHor($aColor,$aWeight);
428    }
429
430    function SetOrder($aDepth) {
431        $this->vert->SetOrder($aDepth);
432        $this->hor->SetOrder($aDepth);
433    }
434
435    function SetPos($aRect) {
436        parent::SetPos($aRect);
437        $this->vert->SetPos($aRect);
438        $this->hor->SetPos($aRect);
439    }
440
441    function SetDensity($aDens) {
442        $this->vert->SetDensity($aDens);
443        $this->hor->SetDensity($aDens);
444    }
445
446    function DoPattern($aImg) {
447        $this->vert->DoPattern($aImg);
448        $this->hor->DoPattern($aImg);
449    }
450}
451
452//=====================================================================
453// Class RectPatternDiagCross
454// Vert/Hor crosses
455//=====================================================================
456
457class RectPatternDiagCross extends RectPattern {
458    private $left=null;
459    private $right=null;
460    function __construct($aColor="black",$aWeight=1) {
461        parent::__construct($aColor,$aWeight);
462        $this->right = new RectPatternRDiag($aColor,$aWeight);
463        $this->left  = new RectPatternLDiag($aColor,$aWeight);
464    }
465
466    function SetOrder($aDepth) {
467        $this->left->SetOrder($aDepth);
468        $this->right->SetOrder($aDepth);
469    }
470
471    function SetPos($aRect) {
472        parent::SetPos($aRect);
473        $this->left->SetPos($aRect);
474        $this->right->SetPos($aRect);
475    }
476
477    function SetDensity($aDens) {
478        $this->left->SetDensity($aDens);
479        $this->right->SetDensity($aDens);
480    }
481
482    function DoPattern($aImg) {
483        $this->left->DoPattern($aImg);
484        $this->right->DoPattern($aImg);
485    }
486
487}
488
489//=====================================================================
490// Class RectPatternFactory
491// Factory class for rectangular pattern
492//=====================================================================
493class RectPatternFactory {
494    function __construct() {
495        // Empty
496    }
497    function Create($aPattern,$aColor,$aWeight=1) {
498        switch($aPattern) {
499            case BAND_RDIAG:
500                $obj =  new RectPatternRDiag($aColor,$aWeight);
501                break;
502            case BAND_LDIAG:
503                $obj =  new RectPatternLDiag($aColor,$aWeight);
504                break;
505            case BAND_SOLID:
506                $obj =  new RectPatternSolid($aColor,$aWeight);
507                break;
508            case BAND_VLINE:
509                $obj =  new RectPatternVert($aColor,$aWeight);
510                break;
511            case BAND_HLINE:
512                $obj =  new RectPatternHor($aColor,$aWeight);
513                break;
514            case BAND_3DPLANE:
515                $obj =  new RectPattern3DPlane($aColor,$aWeight);
516                break;
517            case BAND_HVCROSS:
518                $obj =  new RectPatternCross($aColor,$aWeight);
519                break;
520            case BAND_DIAGCROSS:
521                $obj =  new RectPatternDiagCross($aColor,$aWeight);
522                break;
523            default:
524                JpGraphError::RaiseL(16003,$aPattern);
525                //(" Unknown pattern specification ($aPattern)");
526        }
527        return $obj;
528    }
529}
530
531
532//=====================================================================
533// Class PlotBand
534// Factory class which is used by the client.
535// It is responsible for factoring the corresponding pattern
536// concrete class.
537//=====================================================================
538class PlotBand {
539    public $depth; // Determine if band should be over or under the plots
540    private $prect=null;
541    private $dir, $min, $max;
542
543    function __construct($aDir,$aPattern,$aMin,$aMax,$aColor="black",$aWeight=1,$aDepth=DEPTH_BACK) {
544        $f =  new RectPatternFactory();
545        $this->prect = $f->Create($aPattern,$aColor,$aWeight);
546        if( is_numeric($aMin) && is_numeric($aMax) && ($aMin > $aMax) )
547        JpGraphError::RaiseL(16004);
548        //('Min value for plotband is larger than specified max value. Please correct.');
549        $this->dir = $aDir;
550        $this->min = $aMin;
551        $this->max = $aMax;
552        $this->depth=$aDepth;
553    }
554
555    // Set position. aRect contains absolute image coordinates
556    function SetPos($aRect) {
557        assert( $this->prect != null ) ;
558        $this->prect->SetPos($aRect);
559    }
560
561    function ShowFrame($aFlag=true) {
562        $this->prect->ShowFrame($aFlag);
563    }
564
565    // Set z-order. In front of pplot or in the back
566    function SetOrder($aDepth) {
567        $this->depth=$aDepth;
568    }
569
570    function SetDensity($aDens) {
571        $this->prect->SetDensity($aDens);
572    }
573
574    function GetDir() {
575        return $this->dir;
576    }
577
578    function GetMin() {
579        return $this->min;
580    }
581
582    function GetMax() {
583        return $this->max;
584    }
585
586    function PreStrokeAdjust($aGraph) {
587        // Nothing to do
588    }
589
590    // Display band
591    function Stroke($aImg,$aXScale,$aYScale) {
592        assert( $this->prect != null ) ;
593        if( $this->dir == HORIZONTAL ) {
594            if( $this->min === 'min' ) $this->min = $aYScale->GetMinVal();
595            if( $this->max === 'max' ) $this->max = $aYScale->GetMaxVal();
596
597            // Only draw the bar if it actually appears in the range
598            if ($this->min < $aYScale->GetMaxVal() && $this->max > $aYScale->GetMinVal()) {
599                 
600                // Trucate to limit of axis
601                $this->min = max($this->min, $aYScale->GetMinVal());
602                $this->max = min($this->max, $aYScale->GetMaxVal());
603
604                $x=$aXScale->scale_abs[0];
605                $y=$aYScale->Translate($this->max);
606                $width=$aXScale->scale_abs[1]-$aXScale->scale_abs[0]+1;
607                $height=abs($y-$aYScale->Translate($this->min))+1;
608                $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
609                $this->prect->Stroke($aImg);
610            }
611        }
612        else { // VERTICAL
613            if( $this->min === 'min' ) $this->min = $aXScale->GetMinVal();
614            if( $this->max === 'max' ) $this->max = $aXScale->GetMaxVal();
615
616            // Only draw the bar if it actually appears in the range
617            if ($this->min < $aXScale->GetMaxVal() && $this->max > $aXScale->GetMinVal()) {
618                 
619                // Trucate to limit of axis
620                $this->min = max($this->min, $aXScale->GetMinVal());
621                $this->max = min($this->max, $aXScale->GetMaxVal());
622
623                $y=$aYScale->scale_abs[1];
624                $x=$aXScale->Translate($this->min);
625                $height=abs($aYScale->scale_abs[1]-$aYScale->scale_abs[0]);
626                $width=abs($x-$aXScale->Translate($this->max));
627                $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
628                $this->prect->Stroke($aImg);
629            }
630        }
631    }
632}
633
634
635?>
Note: See TracBrowser for help on using the repository browser.