2 //=======================================================================
3 // File: JPGRAPH_PLOTBAND.PHP
4 // Description: PHP4 Graph Plotting library. Extension module.
6 // Author: Johan Persson (johanp@aditus.nu)
7 // Ver: $Id: jpgraph_plotband.php,v 1.1 2006/07/07 13:37:14 powles Exp $
9 // Copyright (c) Aditus Consulting. All rights reserved.
10 //========================================================================
12 // Constants for types of static bands in plot area
13 DEFINE("BAND_RDIAG",1); // Right diagonal lines
14 DEFINE("BAND_LDIAG",2); // Left diagonal lines
15 DEFINE("BAND_SOLID",3); // Solid one color
16 DEFINE("BAND_VLINE",4); // Vertical lines
17 DEFINE("BAND_HLINE",5); // Horizontal lines
18 DEFINE("BAND_3DPLANE",6); // "3D" Plane
19 DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
20 DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses
23 // Utility class to hold coordinates for a rectangle
27 function Rectangle($aX,$aY,$aWidth,$aHeight) {
32 $this->xe
=$aX+
$aWidth-1;
33 $this->ye
=$aY+
$aHeight-1;
37 //=====================================================================
39 // Base class for pattern hierarchi that is used to display patterned
40 // bands on the graph. Any subclass that doesn't override Stroke()
41 // must at least implement method DoPattern(&$aImg) which is responsible
42 // for drawing the pattern onto the graph.
43 //=====================================================================
49 var $linespacing; // Line spacing in pixels
50 var $iBackgroundColor=-1; // Default is no background fill
52 function RectPattern($aColor,$aWeight=1) {
53 $this->color
= $aColor;
54 $this->weight
= $aWeight;
57 function SetBackground($aBackgroundColor) {
58 $this->iBackgroundColor
=$aBackgroundColor;
61 function SetPos(&$aRect) {
65 function ShowFrame($aShow=true) {
66 $this->doframe
=$aShow;
69 function SetDensity($aDens) {
70 if( $aDens < 1 ||
$aDens > 100 )
71 JpGraphError
::RaiseL(16001,$aDens);
72 //(" Desity for pattern must be between 1 and 100. (You tried $aDens)");
73 // 1% corresponds to linespacing=50
74 // 100 % corresponds to linespacing 1
75 $this->linespacing
= floor(((100-$aDens)/100.0)*50)+
1;
79 function Stroke(&$aImg) {
80 if( $this->rect
== null )
81 JpGraphError
::RaiseL(16002);
82 //(" No positions specified for pattern.");
84 if( !(is_numeric($this->iBackgroundColor
) && $this->iBackgroundColor
==-1) ) {
85 $aImg->SetColor($this->iBackgroundColor
);
86 $aImg->FilledRectangle($this->rect
->x
,$this->rect
->y
,$this->rect
->xe
,$this->rect
->ye
);
89 $aImg->SetColor($this->color
);
90 $aImg->SetLineWeight($this->weight
);
92 // Virtual function implemented by subclass
93 $this->DoPattern($aImg);
95 // Frame around the pattern area
97 $aImg->Rectangle($this->rect
->x
,$this->rect
->y
,$this->rect
->xe
,$this->rect
->ye
);
103 //=====================================================================
104 // Class RectPatternSolid
105 // Implements a solid band
106 //=====================================================================
107 class RectPatternSolid
extends RectPattern
{
109 function RectPatternSolid($aColor="black",$aWeight=1) {
110 parent
::RectPattern($aColor,$aWeight);
113 function DoPattern(&$aImg) {
114 $aImg->SetColor($this->color
);
115 $aImg->FilledRectangle($this->rect
->x
,$this->rect
->y
,
116 $this->rect
->xe
,$this->rect
->ye
);
120 //=====================================================================
121 // Class RectPatternHor
122 // Implements horizontal line pattern
123 //=====================================================================
124 class RectPatternHor
extends RectPattern
{
126 function RectPatternHor($aColor="black",$aWeight=1,$aLineSpacing=7) {
127 parent
::RectPattern($aColor,$aWeight);
128 $this->linespacing
= $aLineSpacing;
131 function DoPattern(&$aImg) {
132 $x0 = $this->rect
->x
;
133 $x1 = $this->rect
->xe
;
135 while( $y < $this->rect
->ye
) {
136 $aImg->Line($x0,$y,$x1,$y);
137 $y +
= $this->linespacing
;
142 //=====================================================================
143 // Class RectPatternVert
144 // Implements vertical line pattern
145 //=====================================================================
146 class RectPatternVert
extends RectPattern
{
147 var $linespacing=10; // Line spacing in pixels
149 function RectPatternVert($aColor="black",$aWeight=1,$aLineSpacing=7) {
150 parent
::RectPattern($aColor,$aWeight);
151 $this->linespacing
= $aLineSpacing;
154 //--------------------
157 function DoPattern(&$aImg) {
159 $y0 = $this->rect
->y
;
160 $y1 = $this->rect
->ye
;
161 while( $x < $this->rect
->xe
) {
162 $aImg->Line($x,$y0,$x,$y1);
163 $x +
= $this->linespacing
;
169 //=====================================================================
170 // Class RectPatternRDiag
171 // Implements right diagonal pattern
172 //=====================================================================
173 class RectPatternRDiag
extends RectPattern
{
174 var $linespacing; // Line spacing in pixels
176 function RectPatternRDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
177 parent
::RectPattern($aColor,$aWeight);
178 $this->linespacing
= $aLineSpacing;
181 function DoPattern(&$aImg) {
182 // --------------------
186 // --------------------
187 $xe = $this->rect
->xe
;
188 $ye = $this->rect
->ye
;
189 $x0 = $this->rect
->x +
round($this->linespacing
/2);
190 $y0 = $this->rect
->y
;
191 $x1 = $this->rect
->x
;
192 $y1 = $this->rect
->y +
round($this->linespacing
/2);
194 while($x0<=$xe && $y1<=$ye) {
195 $aImg->Line($x0,$y0,$x1,$y1);
196 $x0 +
= $this->linespacing
;
197 $y1 +
= $this->linespacing
;
200 if( $xe-$x1 > $ye-$y0 ) {
201 // Width larger than height
202 $x1 = $this->rect
->x +
($y1-$ye);
204 $y0 = $this->rect
->y
;
205 while( $x0 <= $xe ) {
206 $aImg->Line($x0,$y0,$x1,$y1);
207 $x0 +
= $this->linespacing
;
208 $x1 +
= $this->linespacing
;
211 $y0=$this->rect
->y +
($x0-$xe);
215 // Height larger than width
217 $y0 = $diff+
$this->rect
->y
;
219 $x1 = $this->rect
->x
;
220 while( $y1 <= $ye ) {
221 $aImg->Line($x0,$y0,$x1,$y1);
222 $y1 +
= $this->linespacing
;
223 $y0 +
= $this->linespacing
;
228 $x1 = $diff +
$this->rect
->x
;
231 while( $y0 <= $ye ) {
232 $aImg->Line($x0,$y0,$x1,$y1);
233 $y0 +
= $this->linespacing
;
234 $x1 +
= $this->linespacing
;
239 //=====================================================================
240 // Class RectPatternLDiag
241 // Implements left diagonal pattern
242 //=====================================================================
243 class RectPatternLDiag
extends RectPattern
{
244 var $linespacing; // Line spacing in pixels
246 function RectPatternLDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
247 $this->linespacing
= $aLineSpacing;
248 parent
::RectPattern($aColor,$aWeight);
251 function DoPattern(&$aImg) {
252 // --------------------
256 // |------------------|
257 $xe = $this->rect
->xe
;
258 $ye = $this->rect
->ye
;
259 $x0 = $this->rect
->x +
round($this->linespacing
/2);
260 $y0 = $this->rect
->ye
;
261 $x1 = $this->rect
->x
;
262 $y1 = $this->rect
->ye
- round($this->linespacing
/2);
264 while($x0<=$xe && $y1>=$this->rect
->y
) {
265 $aImg->Line($x0,$y0,$x1,$y1);
266 $x0 +
= $this->linespacing
;
267 $y1 -= $this->linespacing
;
269 if( $xe-$x1 > $ye-$this->rect
->y
) {
270 // Width larger than height
271 $x1 = $this->rect
->x +
($this->rect
->y
-$y1);
272 $y0=$ye; $y1=$this->rect
->y
;
273 while( $x0 <= $xe ) {
274 $aImg->Line($x0,$y0,$x1,$y1);
275 $x0 +
= $this->linespacing
;
276 $x1 +
= $this->linespacing
;
279 $y0=$this->rect
->ye
- ($x0-$xe);
283 // Height larger than width
287 while( $y1 >= $this->rect
->y
) {
288 $aImg->Line($x0,$y0,$x1,$y1);
289 $y0 -= $this->linespacing
;
290 $y1 -= $this->linespacing
;
292 $diff = $this->rect
->y
- $y1;
293 $x1 = $this->rect
->x +
$diff;
294 $y1 = $this->rect
->y
;
296 while( $y0 >= $this->rect
->y
) {
297 $aImg->Line($x0,$y0,$x1,$y1);
298 $y0 -= $this->linespacing
;
299 $x1 +
= $this->linespacing
;
304 //=====================================================================
305 // Class RectPattern3DPlane
306 // Implements "3D" plane pattern
307 //=====================================================================
308 class RectPattern3DPlane
extends RectPattern
{
309 var $alpha=50; // Parameter that specifies the distance
310 // to "simulated" horizon in pixel from the
311 // top of the band. Specifies how fast the lines
314 function RectPattern3DPlane($aColor="black",$aWeight=1) {
315 parent
::RectPattern($aColor,$aWeight);
316 $this->SetDensity(10); // Slightly larger default
319 function SetHorizon($aHorizon) {
320 $this->alpha
=$aHorizon;
323 function DoPattern(&$aImg) {
324 // "Fake" a nice 3D grid-effect.
325 $x0 = $this->rect
->x +
$this->rect
->w
/2;
326 $y0 = $this->rect
->y
;
328 $y1 = $this->rect
->ye
;
332 // BTW "apa" means monkey in Swedish but is really a shortform for
333 // "alpha+a" which was the labels I used on paper when I derived the
334 // geometric to get the 3D perspective right.
335 // $apa is the height of the bounding rectangle plus the distance to the
336 // artifical horizon (alpha)
337 $apa = $this->rect
->h +
$this->alpha
;
339 // Three cases and three loops
340 // 1) The endpoint of the line ends on the bottom line
341 // 2) The endpoint ends on the side
342 // 3) Horizontal lines
344 // Endpoint falls on bottom line
345 $middle=$this->rect
->x +
$this->rect
->w
/2;
346 $dist=$this->linespacing
;
347 $factor=$this->alpha
/($apa);
348 while($x1>$this->rect
->x
) {
349 $aImg->Line($x0,$y0,$x1,$y1);
350 $aImg->Line($x0_right,$y0,$x1_right,$y1);
351 $x1 = $middle - $dist;
352 $x0 = $middle - $dist * $factor;
353 $x1_right = $middle +
$dist;
354 $x0_right = $middle +
$dist * $factor;
355 $dist +
= $this->linespacing
;
358 // Endpoint falls on sides
359 $dist -= $this->linespacing
;
361 $c = $apa - $d*$apa/$dist;
362 while( $x0>$this->rect
->x
) {
363 $aImg->Line($x0,$y0,$this->rect
->x
,$this->rect
->ye
-$c);
364 $aImg->Line($x0_right,$y0,$this->rect
->xe
,$this->rect
->ye
-$c);
365 $dist +
= $this->linespacing
;
366 $x0 = $middle - $dist * $factor;
367 $x1 = $middle - $dist;
368 $x0_right = $middle +
$dist * $factor;
369 $c = $apa - $d*$apa/$dist;
373 // They need some serious consideration since they are a function
374 // of perspective depth (alpha) and density (linespacing)
379 // The first line is drawn directly. Makes the loop below slightly
381 $aImg->Line($x0,$y,$x1,$y);
382 $hls = $this->linespacing
;
384 // A correction factor for vertical "brick" line spacing to account for
385 // a) the difference in number of pixels hor vs vert
386 // b) visual apperance to make the first layer of "bricks" look more
388 $vls = $this->linespacing
*0.6;
390 $ds = $hls*($apa-$vls)/$apa;
391 // Get the slope for the "perspective line" going from bottom right
392 // corner to top left corner of the "first" brick.
394 // Uncomment the following lines if you want to get a visual understanding
395 // of what this helpline does. BTW this mimics the way you would get the
396 // perspective right when drawing on paper.
399 $y0 = $this->rect->ye;
400 $len=floor(($this->rect->ye-$this->rect->y)/$vls);
401 $x1 = $middle+round($len*$ds);
402 $y1 = $this->rect->ye-$len*$vls;
403 $aImg->PushColor("red");
404 $aImg->Line($x0,$y0,$x1,$y1);
409 $k=($this->rect
->ye
-($this->rect
->ye
-$vls))/($middle-($middle-$ds));
411 while( $y>$this->rect
->y
) {
412 $aImg->Line($this->rect
->x
,$y,$this->rect
->xe
,$y);
413 $adj = $k*$dist/(1+
$dist*$k/$apa);
414 if( $adj < 2 ) $adj=1;
415 $y = $this->rect
->ye
- round($adj);
421 //=====================================================================
422 // Class RectPatternCross
424 //=====================================================================
425 class RectPatternCross
extends RectPattern
{
428 function RectPatternCross($aColor="black",$aWeight=1) {
429 parent
::RectPattern($aColor,$aWeight);
430 $this->vert
= new RectPatternVert($aColor,$aWeight);
431 $this->hor
= new RectPatternHor($aColor,$aWeight);
434 function SetOrder($aDepth) {
435 $this->vert
->SetOrder($aDepth);
436 $this->hor
->SetOrder($aDepth);
439 function SetPos(&$aRect) {
440 parent
::SetPos($aRect);
441 $this->vert
->SetPos($aRect);
442 $this->hor
->SetPos($aRect);
445 function SetDensity($aDens) {
446 $this->vert
->SetDensity($aDens);
447 $this->hor
->SetDensity($aDens);
450 function DoPattern(&$aImg) {
451 $this->vert
->DoPattern($aImg);
452 $this->hor
->DoPattern($aImg);
456 //=====================================================================
457 // Class RectPatternDiagCross
459 //=====================================================================
461 class RectPatternDiagCross
extends RectPattern
{
464 function RectPatternDiagCross($aColor="black",$aWeight=1) {
465 parent
::RectPattern($aColor,$aWeight);
466 $this->right
= new RectPatternRDiag($aColor,$aWeight);
467 $this->left
= new RectPatternLDiag($aColor,$aWeight);
470 function SetOrder($aDepth) {
471 $this->left
->SetOrder($aDepth);
472 $this->right
->SetOrder($aDepth);
475 function SetPos(&$aRect) {
476 parent
::SetPos($aRect);
477 $this->left
->SetPos($aRect);
478 $this->right
->SetPos($aRect);
481 function SetDensity($aDens) {
482 $this->left
->SetDensity($aDens);
483 $this->right
->SetDensity($aDens);
486 function DoPattern(&$aImg) {
487 $this->left
->DoPattern($aImg);
488 $this->right
->DoPattern($aImg);
493 //=====================================================================
494 // Class RectPatternFactory
495 // Factory class for rectangular pattern
496 //=====================================================================
497 class RectPatternFactory
{
498 function RectPatternFactory() {
501 function Create($aPattern,$aColor,$aWeight=1) {
504 $obj = new RectPatternRDiag($aColor,$aWeight);
507 $obj = new RectPatternLDiag($aColor,$aWeight);
510 $obj = new RectPatternSolid($aColor,$aWeight);
513 $obj = new RectPatternVert($aColor,$aWeight);
516 $obj = new RectPatternHor($aColor,$aWeight);
519 $obj = new RectPattern3DPlane($aColor,$aWeight);
522 $obj = new RectPatternCross($aColor,$aWeight);
525 $obj = new RectPatternDiagCross($aColor,$aWeight);
528 JpGraphError
::RaiseL(16003,$aPattern);
529 //(" Unknown pattern specification ($aPattern)");
536 //=====================================================================
538 // Factory class which is used by the client.
539 // It is responsible for factoring the corresponding pattern
541 //=====================================================================
545 var $dir, $min, $max;
547 function PlotBand($aDir,$aPattern,$aMin,$aMax,$aColor="black",$aWeight=1,$aDepth=DEPTH_BACK
) {
548 $f = new RectPatternFactory();
549 $this->prect
= $f->Create($aPattern,$aColor,$aWeight);
550 if( is_numeric($aMin) && is_numeric($aMax) && ($aMin > $aMax) )
551 JpGraphError
::RaiseL(16004);
552 //('Min value for plotband is larger than specified max value. Please correct.');
556 $this->depth
=$aDepth;
559 // Set position. aRect contains absolute image coordinates
560 function SetPos(&$aRect) {
561 assert( $this->prect
!= null ) ;
562 $this->prect
->SetPos($aRect);
565 function ShowFrame($aFlag=true) {
566 $this->prect
->ShowFrame($aFlag);
569 // Set z-order. In front of pplot or in the back
570 function SetOrder($aDepth) {
571 $this->depth
=$aDepth;
574 function SetDensity($aDens) {
575 $this->prect
->SetDensity($aDens);
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();
597 // Only draw the bar if it actually appears in the range
598 if ($this->min
< $aYScale->GetMaxVal() && $this->max
> $aYScale->GetMinVal()) {
600 // Trucate to limit of axis
601 $this->min
= max($this->min
, $aYScale->GetMinVal());
602 $this->max
= min($this->max
, $aYScale->GetMaxVal());
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);
613 if( $this->min
=== 'min' ) $this->min
= $aXScale->GetMinVal();
614 if( $this->max
=== 'max' ) $this->max
= $aXScale->GetMaxVal();
616 // Only draw the bar if it actually appears in the range
617 if ($this->min
< $aXScale->GetMaxVal() && $this->max
> $aXScale->GetMinVal()) {
619 // Trucate to limit of axis
620 $this->min
= max($this->min
, $aXScale->GetMinVal());
621 $this->max
= min($this->max
, $aXScale->GetMaxVal());
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);