]> git.sur5r.net Git - bacula/bacula/blob - gui/bacula-web/external_packages/phplot/phplot.php
Initial revision
[bacula/bacula] / gui / bacula-web / external_packages / phplot / phplot.php
1 <?php
2
3 /* $Id$ */
4
5 /*
6  * Copyright (C) 1998, 1999, 2000, 2001, 2002 Afan Ottenheimer.  Released under
7  * the GPL and PHP licenses as stated in the the README file which
8  * should have been included with this document.
9  *
10  * Recent (2003-2004) work by Miguel de Benito Delgado <nonick AT 8027 DOT org>
11  *
12  */
13
14
15 // PHPLOT Version 5.0.rc1
16 // Requires PHP 4.1.0 or later (CHECK THIS)
17
18 if (! defined(__FUNCTION__))
19     define(__FUNCTION__, '__FUNCTION__ Requires at least PHP 4.3.0.');
20
21 define ('MINY', -1);        // Indexes in $data (for DrawXDataLine())
22 define ('MAXY', -2);
23
24 error_reporting(E_ALL);
25
26
27 class PHPlot {
28
29     /* I have removed internal variable declarations, some isset() checking was required, 
30      * but now the variables left are those which can be tweaked by the user. This is intended to
31      * be the first step towards moving most of the Set...() methods into a subclass which will be 
32      * used only when strictly necessary. Many users will be able to put default values here in the
33      * class and thus avoid memory overhead and reduce parsing times.
34      */
35     //////////////// CONFIG PARAMETERS //////////////////////
36         var $reduction = 8;                                     // BW Patch
37     var $is_inline = FALSE;             // FALSE = Sends headers, TRUE = sends just raw image data
38     var $browser_cache = FALSE;         // FALSE = Sends headers for browser to not cache the image,
39                                         // (only if is_inline = FALSE also)
40
41     var $safe_margin = 5;               // Extra margin used in several places. In pixels
42
43     var $x_axis_position = '';          // Where to draw both axis (world coordinates),
44     var $y_axis_position = '';          // leave blank for X axis at 0 and Y axis at left of plot.
45
46     var $xscale_type = 'linear';        // linear, log
47     var $yscale_type = 'linear';
48
49 //Fonts
50     var $use_ttf  = FALSE;                  // Use True Type Fonts?
51     var $ttf_path = '.';                    // Default path to look in for TT Fonts. 
52     var $default_ttfont = 'benjamingothic.ttf';
53     var $line_spacing = 4;                  // Pixels between lines.
54
55     // Font angles: 0 or 90 degrees for fixed fonts, any for TTF
56     var $x_label_angle = 0;                 // For labels on X axis (tick and data)
57     var $y_label_angle = 0;                 // For labels on Y axis (tick and data)
58     var $x_title_angle = 0;                 // Don't change this if you don't want to screw things up!
59     var $y_title_angle = 90;                // Nor this.
60     var $title_angle = 0;                   // Or this.
61
62 //Formats
63     var $file_format = 'png';
64     var $output_file = '';                  // For output to a file instead of stdout
65
66 //Data
67     var $data_type = 'text-data';           // text-data, data-data-error, data-data, text-data-pie
68     var $plot_type= 'linepoints';           // bars, lines, linepoints, area, points, pie, thinbarline, squared
69
70     var $label_scale_position = 0.5;        // Shifts data labes in pie charts. 1 = top, 0 = bottom
71     var $group_frac_width = 0.7;            // value from 0 to 1 = width of bar groups
72     var $bar_width_adjust = 1;              // 1 = bars of normal width, must be > 0
73
74     var $y_precision = 1;
75     var $x_precision = 1;
76
77     var $data_units_text = '';              // Units text for 'data' labels (i.e: 'ยค', '$', etc.)
78
79 // Titles
80     var $title_txt = '';
81
82     var $x_title_txt = '';
83     var $x_title_pos = 'plotdown';          // plotdown, plotup, both, none
84
85     var $y_title_txt = '';
86     var $y_title_pos = 'plotleft';          // plotleft, plotright, both, none
87
88
89 //Labels
90     // There are two types of labels in PHPlot:
91     //    Tick labels: they follow the grid, next to ticks in axis.   (DONE)
92     //                 they are drawn at grid drawing time, by _DrawXTicks() and _DrawYTicks()
93     //    Data labels: they follow the data points, and can be placed on the axis or the plot (x/y)  (TODO)
94     //                 they are drawn at graph plotting time, by DrawDataLabel(), called by DrawLines(), etc.
95     //                 Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_line
96
97     // Tick Labels
98     var $x_tick_label_pos = 'plotdown';     // plotdown, plotup, both, xaxis, none
99     var $y_tick_label_pos = 'plotleft';     // plotleft, plotright, both, yaxis, none
100
101     // Data Labels:
102     var $x_data_label_pos = 'plotdown';     // plotdown, plotup, both, plot, all, none
103     var $y_data_label_pos = 'plotleft';     // plotleft, plotright, both, plot, all, none
104
105     var $draw_x_data_label_lines = FALSE;   // Draw a line from the data point to the axis?
106     var $draw_y_data_label_lines = FALSE;   // TODO
107
108     // Label types: (for tick, data and plot labels)
109     var $x_label_type = '';                 // data, time. Leave blank for no formatting.
110     var $y_label_type = '';                 // data, time. Leave blank for no formatting.
111     var $x_time_format = '%H:%m:%s';        // See http://www.php.net/manual/html/function.strftime.html
112     var $y_time_format = '%H:%m:%s';        // SetYTimeFormat() too... 
113
114     // Skipping labels
115     var $x_label_inc = 1;                   // Draw a label every this many (1 = all) (TODO)
116     var $y_label_inc = 1;
117
118     // Legend
119     var $legend = '';                       // An array with legend titles
120     var $legend_x_pos = '';
121     var $legend_y_pos = '';
122
123
124 //Ticks
125     var $x_tick_length = 5;                 // tick length in pixels for upper/lower axis
126     var $y_tick_length = 5;                 // tick length in pixels for left/right axis
127
128     var $x_tick_cross = 3;                  // ticks cross x axis this many pixels
129     var $y_tick_cross = 3;                  // ticks cross y axis this many pixels
130
131     var $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none 
132     var $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none 
133
134     var $num_x_ticks = '';
135     var $num_y_ticks = '';
136
137     var $x_tick_increment = '';             // Set num_x_ticks or x_tick_increment, not both.
138     var $y_tick_increment = '';             // Set num_y_ticks or y_tick_increment, not both.
139
140     var $skip_top_tick = FALSE;
141     var $skip_bottom_tick = FALSE;
142     var $skip_left_tick = FALSE;
143     var $skip_right_tick = FALSE;
144
145 //Grid Formatting
146     var $draw_x_grid = FALSE;
147     var $draw_y_grid = TRUE;
148
149     var $dashed_grid = TRUE;
150
151 //Colors and styles       (all colors can be array (R,G,B) or named color)
152     var $color_array = 'small';             // 'small', 'large' or array (define your own colors)
153                                             // See rgb.inc.php and SetRGBArray()
154     var $i_border = array(194, 194, 194);
155     var $plot_bg_color = 'white';
156     var $bg_color = 'white';
157     var $label_color = 'black';
158     var $text_color = 'black';
159     var $grid_color = 'black';
160     var $light_grid_color = 'gray';
161     var $tick_color = 'black';
162     var $title_color = 'black';
163     var $data_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
164     var $error_bar_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
165     var $data_border_colors = array('black');
166
167     var $line_widths = 1;                  // single value or array
168     var $line_styles = array('solid', 'solid', 'dashed');   // single value or array
169     var $dashed_style = '2-4';              // colored dots-transparent dots
170
171     var $point_size = 5;
172     var $point_shape = 'diamond';           // rect, circle, diamond, triangle, dot, line, halfline, cross
173
174     var $error_bar_size = 5;                // right and left size of tee
175     var $error_bar_shape = 'tee';           // 'tee' or 'line'
176     var $error_bar_line_width = 1;          // single value (or array TODO)
177
178     var $plot_border_type = 'sides';        // left, sides, none, full
179     var $image_border_type = 'none';        // 'raised', 'plain', 'none'
180
181     var $shading = 5;                       // 0 for no shading, > 0 is size of shadows in pixels
182
183     var $draw_plot_area_background = FALSE;
184     var $draw_broken_lines = FALSE;          // Tells not to draw lines for missing Y data.
185
186
187 //////////////////////////////////////////////////////
188 //BEGIN CODE
189 //////////////////////////////////////////////////////
190
191     /*!
192      * Constructor: Setup img resource, colors and size of the image, and font sizes.
193      * 
194      * \param which_width       int    Image width in pixels.
195      * \param which_height      int    Image height in pixels.
196      * \param which_output_file string Filename for output.
197      * \param which_input_fule  string Path to a file to be used as background.
198      */
199     function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) 
200     {
201         /*
202          * Please see http://www.php.net/register_shutdown_function
203          * PLEASE NOTE: register_shutdown_function() will take a copy of the object rather than a reference
204          * so we put an ampersand. However, the function registered will work on the object as it
205          * was upon registration. To solve this, one of two methods can be used:
206          *      $obj = new object();
207          *      register_shutdown_function(array(&$obj,'shutdown'));
208          * OR
209          *      $obj = &new object();
210          * HOWEVER, as the second statement assigns $obj a reference to the current object, it might be that
211          * several instances mess things up... (CHECK THIS)
212          *
213          * AND
214          *    as $this->img is set upon construction of the object, problems will not arise for us (for the
215          *    moment maybe, so I put all this here just in case)
216          */
217         register_shutdown_function(array(&$this, '_PHPlot'));
218
219         $this->SetRGBArray($this->color_array);
220
221         $this->background_done = FALSE;     // Set to TRUE after background image first drawn
222
223         if ($which_output_file)
224             $this->SetOutputFile($which_output_file);
225
226         if ($which_input_file)
227             $this->SetInputFile($which_input_file);
228         else {
229             $this->image_width = $which_width;
230             $this->image_height = $which_height;
231
232             $this->img = ImageCreate($this->image_width, $this->image_height);
233             if (! $this->img)
234                 $this->PrintError('PHPlot(): Could not create image resource.');
235
236         }
237
238         $this->SetDefaultStyles();
239         $this->SetDefaultFonts();
240
241         $this->SetTitle('');
242         $this->SetXTitle('');
243         $this->SetYTitle('');
244
245         $this->print_image = TRUE;      // Use for multiple plots per image (TODO: automatic)
246     }
247
248     /*!
249      * Destructor. Image resources not deallocated can be memory hogs, I think
250      * it is safer to automatically call imagedestroy upon script termination than
251      * do it ourselves.
252      * See notes in the constructor code.
253      */
254     function _PHPlot () 
255     {
256         ImageDestroy($this->img);
257         return;
258     }
259
260
261 /////////////////////////////////////////////    
262 //////////////                         COLORS
263 /////////////////////////////////////////////
264
265     /*!
266      * Returns an index to a color passed in as anything (string, hex, rgb)
267      *
268      * \param which_color * Color (can be '#AABBCC', 'Colorname', or array(r,g,b))
269      */
270     function SetIndexColor($which_color) 
271     {
272         list ($r, $g, $b) = $this->SetRGBColor($which_color);  //Translate to RGB
273         $index = ImageColorExact($this->img, $r, $g, $b);
274         if ($index == -1) {
275             return ImageColorResolve($this->img, $r, $g, $b);
276         } else {
277             return $index;
278         }
279     }
280
281
282     /*!
283      * Returns an index to a slightly darker color than the one requested. 
284      */
285     function SetIndexDarkColor($which_color) 
286     {
287         list ($r, $g, $b) = $this->SetRGBColor($which_color);
288
289         $r -= 0x30;     $r = ($r < 0) ? 0 : $r;
290         $g -= 0x30;     $g = ($g < 0) ? 0 : $g;
291         $b -= 0x30;     $b = ($b < 0) ? 0 : $b;
292
293         $index = ImageColorExact($this->img, $r, $g, $b);
294         if ($index == -1) {
295             return ImageColorResolve($this->img, $r, $g, $b);
296         } else {
297             return $index;
298         }
299     }
300
301     /*!
302      * Sets/reverts all colors and styles to their defaults. If session is set, then only updates indices,
303      * as they are lost with every script execution, else, sets the default colors by name or value and
304      * then updates indices too.
305      *
306      * FIXME Isn't this too slow?
307      *
308      */
309     function SetDefaultStyles() 
310     {
311         /* Some of the Set*() functions use default values when they get no parameters. */
312
313         if (! isset($this->session_set)) {
314             // If sessions are enabled, this variable will be preserved, so upon future executions, we
315             // will have it set, as well as color names (though not color indices, that's why we 
316             // need to rebuild them)
317             $this->session_set = TRUE;
318
319             // These only need to be set once
320             $this->SetLineWidths();
321             $this->SetLineStyles();
322             $this->SetDefaultDashedStyle($this->dashed_style);
323             $this->SetPointSize($this->point_size);
324         }
325
326         $this->SetImageBorderColor($this->i_border);
327         $this->SetPlotBgColor($this->plot_bg_color);
328         $this->SetBackgroundColor($this->bg_color);
329         $this->SetLabelColor($this->label_color);
330         $this->SetTextColor($this->text_color);
331         $this->SetGridColor($this->grid_color);
332         $this->SetLightGridColor($this->light_grid_color);
333         $this->SetTickColor($this->tick_color);
334         $this->SetTitleColor($this->title_color);
335         $this->SetDataColors();
336         $this->SetErrorBarColors();
337         $this->SetDataBorderColors();
338     }
339
340
341     /*
342      *
343      */
344     function SetBackgroundColor($which_color) 
345     {
346         $this->bg_color= $which_color;
347         $this->ndx_bg_color= $this->SetIndexColor($this->bg_color);
348         return TRUE;
349     }
350
351     /*
352      *
353      */
354     function SetPlotBgColor($which_color) 
355     {
356         $this->plot_bg_color= $which_color;
357         $this->ndx_plot_bg_color= $this->SetIndexColor($this->plot_bg_color);
358         return TRUE;
359     }
360
361    /*
362     *
363     */
364     function SetTitleColor($which_color) 
365     {
366         $this->title_color= $which_color;
367         $this->ndx_title_color= $this->SetIndexColor($this->title_color);
368         return TRUE;
369     }
370
371     /*
372      *
373      */
374     function SetTickColor ($which_color) 
375     {
376         $this->tick_color= $which_color;
377         $this->ndx_tick_color= $this->SetIndexColor($this->tick_color);
378         return TRUE;
379     }
380
381     
382     /*
383      *
384      */
385     function SetLabelColor ($which_color) 
386     {
387         $this->label_color = $which_color;
388         $this->ndx_title_color= $this->SetIndexColor($this->label_color);
389         return TRUE;
390     }
391
392     
393     /*
394      *
395      */
396     function SetTextColor ($which_color) 
397     {
398         $this->text_color= $which_color;
399         $this->ndx_text_color= $this->SetIndexColor($this->text_color);
400         return TRUE;
401     }
402
403     
404     /*
405      *
406      */
407     function SetLightGridColor ($which_color) 
408     {
409         $this->light_grid_color= $which_color;
410         $this->ndx_light_grid_color= $this->SetIndexColor($this->light_grid_color);
411         return TRUE;
412     }
413
414     
415     /*
416      *
417      */
418     function SetGridColor ($which_color) 
419     {
420         $this->grid_color = $which_color;
421         $this->ndx_grid_color= $this->SetIndexColor($this->grid_color);
422         return TRUE;
423     }
424
425     
426     /*
427      *
428      */
429     function SetImageBorderColor($which_color)
430     {
431         $this->i_border = $which_color;
432         $this->ndx_i_border = $this->SetIndexColor($this->i_border);
433         $this->ndx_i_border_dark = $this->SetIndexDarkColor($this->i_border);
434         return TRUE;
435     }
436
437     
438     /*
439      *
440      */   
441     function SetTransparentColor($which_color) 
442     { 
443         ImageColorTransparent($this->img, $this->SetIndexColor($which_color));
444         return TRUE;
445     }
446     
447
448     /*!
449      * Sets the array of colors to be used. It can be user defined, a small predefined one 
450      * or a large one included from 'rgb.inc.php'.
451      *
452      * \param which_color_array If an array, the used as color array. If a string can 
453      *        be one of 'small' or 'large'.
454      */
455     function SetRGBArray ($which_color_array) 
456     { 
457         if ( is_array($which_color_array) ) {           // User defined array
458             $this->rgb_array = $which_color_array;
459             return TRUE;
460         } elseif ($which_color_array == 'small') {      // Small predefined color array
461             $this->rgb_array = array(
462                 'white'          => array(255, 255, 255),
463                 'snow'           => array(255, 250, 250),
464                 'PeachPuff'      => array(255, 218, 185),
465                 'ivory'          => array(255, 255, 240),
466                 'lavender'       => array(230, 230, 250),
467                 'black'          => array(  0,   0,   0),
468                 'DimGrey'        => array(105, 105, 105),
469                 'gray'           => array(190, 190, 190),
470                 'grey'           => array(190, 190, 190),
471                 'navy'           => array(  0,   0, 128),
472                 'SlateBlue'      => array(106,  90, 205),
473                 'blue'           => array(  0,   0, 255),
474                 'SkyBlue'        => array(135, 206, 235),
475                 'cyan'           => array(  0, 255, 255),
476                 'DarkGreen'      => array(  0, 100,   0),
477                 'green'          => array(  0, 255,   0),
478                 'YellowGreen'    => array(154, 205,  50),
479                 'yellow'         => array(255, 255,   0),
480                 'orange'         => array(255, 165,   0),
481                 'gold'           => array(255, 215,   0),
482                 'peru'           => array(205, 133,  63),
483                 'beige'          => array(245, 245, 220),
484                 'wheat'          => array(245, 222, 179),
485                 'tan'            => array(210, 180, 140),
486                 'brown'          => array(165,  42,  42),
487                 'salmon'         => array(250, 128, 114),
488                 'red'            => array(255,   0,   0),
489                 'pink'           => array(255, 192, 203),
490                 'maroon'         => array(176,  48,  96),
491                 'magenta'        => array(255,   0, 255),
492                 'violet'         => array(238, 130, 238),
493                 'plum'           => array(221, 160, 221),
494                 'orchid'         => array(218, 112, 214),
495                 'purple'         => array(160,  32, 240),
496                 'azure1'         => array(240, 255, 255),
497                 'aquamarine1'    => array(127, 255, 212)
498                 );
499             return TRUE;
500         } elseif ($which_color_array === 'large')  {    // Large color array
501             include("./rgb.inc.php");
502             $this->rgb_array = $RGBArray;
503         } else {                                        // Default to black and white only.
504             $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0));
505         }
506
507         return TRUE;
508     }
509
510     /*!
511      * Returns an array in R, G, B format 0-255
512      *
513      *  \param color_asked array(R,G,B) or string (named color or '#AABBCC')
514      */
515     function SetRGBColor($color_asked) 
516     {
517         if ($color_asked == '') { $color_asked = array(0, 0, 0); };
518
519         if ( count($color_asked) == 3 ) {    // already array of 3 rgb
520                $ret_val =  $color_asked;
521         } else {                             // asking for a color by string
522             if(substr($color_asked, 0, 1) == '#') {         // asking in #FFFFFF format. 
523                 $ret_val = array(hexdec(substr($color_asked, 1, 2)), hexdec(substr($color_asked, 3, 2)), 
524                                   hexdec(substr($color_asked, 5, 2)));
525             } else {                                        // asking by color name
526                 $ret_val = $this->rgb_array[$color_asked];
527             }
528         }
529         return $ret_val;
530     }
531
532
533     /*!
534      * Sets the colors for the data.
535      */
536     function SetDataColors($which_data = NULL, $which_border = NULL) 
537     {
538         if (is_null($which_data) && is_array($this->data_colors)) {
539             // use already set data_colors
540         } else if (! is_array($which_data)) {
541             $this->data_colors = ($which_data) ? array($which_data) : array('blue', 'red', 'green', 'orange');
542         } else {
543             $this->data_colors = $which_data;
544         }
545
546         $i = 0;
547         foreach ($this->data_colors as $col) {
548             $this->ndx_data_colors[$i] = $this->SetIndexColor($col);
549             $this->ndx_data_dark_colors[$i] = $this->SetIndexDarkColor($col);
550             $i++;
551         }
552
553         // For past compatibility:
554         $this->SetDataBorderColors($which_border);
555     } // function SetDataColors()
556
557
558     /*!
559      *
560      */
561     function SetDataBorderColors($which_br = NULL)
562     {
563         if (is_null($which_br) && is_array($this->data_border_colors)) {
564             // use already set data_border_colors
565         } else if (! is_array($which_br)) {
566             // Create new array with specified color
567             $this->data_border_colors = ($which_br) ? array($which_br) : array('black');
568         } else {
569             $this->data_border_colors = $which_br;
570         }
571
572         $i = 0;
573         foreach($this->data_border_colors as $col) {
574             $this->ndx_data_border_colors[$i] = $this->SetIndexColor($col);
575             $i++;
576         }
577     } // function SetDataBorderColors()
578
579
580     /*!
581      * Sets the colors for the data error bars.
582      */
583     function SetErrorBarColors($which_err = NULL)
584     {
585         if (is_null($which_err) && is_array($this->error_bar_colors)) {
586             // use already set error_bar_colors
587         } else if (! is_array($which_err)) {
588             $this->error_bar_colors = ($which_err) ? array($which_err) : array('black');
589         } else {
590             $this->error_bar_colors = $which_err;
591         }
592
593         $i = 0;
594         foreach($this->error_bar_colors as $col) {
595             $this->ndx_error_bar_colors[$i] = $this->SetIndexColor($col);
596             $i++;
597         }       
598         return TRUE;
599
600     } // function SetErrorBarColors()
601
602
603     /*!
604      * Sets the default dashed style.
605      *  \param which_style A string specifying order of colored and transparent dots, 
606      *         i.e: '4-3' means 4 colored, 3 transparent; 
607      *              '2-3-1-2' means 2 colored, 3 transparent, 1 colored, 2 transparent.
608      */
609     function SetDefaultDashedStyle($which_style) 
610     {
611         // String: "numcol-numtrans-numcol-numtrans..."
612         $asked = explode('-', $which_style);
613
614         if (count($asked) < 2) {
615             $this->DrawError("SetDefaultDashedStyle(): Wrong parameter '$which_style'.");
616             return FALSE;
617         }
618
619         // Build the string to be eval()uated later by SetDashedStyle()
620         $this->default_dashed_style = 'array( ';
621
622         $t = 0;
623         foreach($asked as $s) {
624             if ($t % 2 == 0) {
625                 $this->default_dashed_style .= str_repeat('$which_ndxcol,', $s);
626             } else {
627                 $this->default_dashed_style .= str_repeat('IMG_COLOR_TRANSPARENT,', $s);
628             }
629             $t++;
630         }
631         // Remove trailing comma and add closing parenthesis
632         $this->default_dashed_style = substr($this->default_dashed_style, 0, -1);
633         $this->default_dashed_style .= ')';
634
635         return TRUE;
636     }
637
638
639     /*!
640      * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style
641      *   \param which_ndxcol Color index to be used.
642      */
643     function SetDashedStyle($which_ndxcol)
644     {
645         // See SetDefaultDashedStyle() to understand this.
646         eval ("\$style = $this->default_dashed_style;");
647         return imagesetstyle($this->img, $style);
648     }
649
650
651     /*!
652      * Sets line widths on a per-line basis.
653      */
654     function SetLineWidths($which_lw=NULL) 
655     {
656         if (is_null($which_lw)) {
657             // Do nothing, use default value.
658         } else if (is_array($which_lw)) {
659             // Did we get an array with line widths?
660             $this->line_widths = $which_lw;
661         } else {
662             $this->line_widths = array($which_lw);
663         }
664         return TRUE;
665     }
666
667     /*!
668      *
669      */
670     function SetLineStyles($which_ls=NULL)
671     {
672         if (is_null($which_ls)) {
673             // Do nothing, use default value.
674         } else if (! is_array($which_ls)) {
675             // Did we get an array with line styles?
676             $this->line_styles = $which_ls;
677         } else {
678             $this->line_styles = ($which_ls) ? array($which_ls) : array('solid');
679         }
680         return TRUE;
681     }
682
683
684 /////////////////////////////////////////////
685 //////////////                          FONTS
686 /////////////////////////////////////////////
687
688
689     /*!
690      * Sets number of pixels between lines of the same text.
691      */
692     function SetLineSpacing($which_spc) 
693     {
694         $this->line_spacing = $which_spc;
695     }
696
697
698     /*!
699      * Enables use of TrueType fonts in the graph. Font initialisation methods
700      * depend on this setting, so when called, SetUseTTF() resets the font
701      * settings
702      */
703     function SetUseTTF($which_ttf) 
704     {
705         $this->use_ttf = $which_ttf;
706         if ($which_ttf)
707             $this->SetDefaultFonts();
708         return TRUE;
709     }
710
711     /*!
712      * Sets the directory name to look into for TrueType fonts.
713      */
714     function SetTTFPath($which_path)
715     {
716         // Maybe someone needs really dynamic config. He'll need this:
717         // clearstatcache();
718
719         if (is_dir($which_path) && is_readable($which_path)) {
720             $this->ttf_path = $which_path;
721             return TRUE;
722         } else {
723             $this->PrintError("SetTTFPath(): $which_path is not a valid path.");
724             return FALSE;
725         }
726     }
727
728     /*!
729      * Sets the default TrueType font and updates all fonts to that.
730      */
731     function SetDefaultTTFont($which_font) 
732     {
733         if (is_file($which_font) && is_readable($which_font)) {
734             $this->default_ttfont = $which_font;
735             return $this->SetDefaultFonts();
736         } else {
737             $this->PrintError("SetDefaultTTFont(): $which_font is not a valid font file.");
738             return FALSE;
739         }
740     }
741
742     /*!
743      * Sets fonts to their defaults
744      */
745     function SetDefaultFonts() 
746     {
747         // TTF:
748         if ($this->use_ttf) {
749             //$this->SetTTFPath(dirname($_SERVER['PHP_SELF']));
750             $this->SetTTFPath(getcwd());
751             $this->SetFont('generic', $this->default_ttfont, 8);
752             $this->SetFont('title', $this->default_ttfont, 14);
753             $this->SetFont('legend', $this->default_ttfont, 8);
754             $this->SetFont('x_label', $this->default_ttfont, 6);
755             $this->SetFont('y_label', $this->default_ttfont, 6);
756             $this->SetFont('x_title', $this->default_ttfont, 10);
757             $this->SetFont('y_title', $this->default_ttfont, 10);
758         } 
759         // Fixed:
760         else {
761             $this->SetFont('generic', 2);
762             $this->SetFont('title', 5);
763             $this->SetFont('legend', 2);
764             $this->SetFont('x_label', 1);
765             $this->SetFont('y_label', 1);           
766             $this->SetFont('x_title', 3);
767             $this->SetFont('y_title', 3);
768         }
769
770         return TRUE;
771     }
772
773     /*!
774      * Sets Fixed/Truetype font parameters.
775      *  \param $which_elem Is the element whose font is to be changed.
776      *         It can be one of 'title', 'legend', 'generic', 
777      *         'x_label', 'y_label', x_title' or 'y_title'
778      *  \param $which_font Can be a number (for fixed font sizes) or 
779      *         a string with the filename when using TTFonts.
780      *  \param $which_size Point size (TTF only)
781      * Calculates and updates internal height and width variables.
782      */
783     function SetFont($which_elem, $which_font, $which_size = 12) 
784     {
785         // TTF:
786         if ($this->use_ttf) {
787             $path = $this->ttf_path.'/'.$which_font;
788
789             if (! is_file($path) || ! is_readable($path) ) {
790                 $this->DrawError("SetFont(): True Type font $path doesn't exist");
791                 return FALSE;
792             }
793
794             switch ($which_elem) {
795             case 'generic':
796                 $this->generic_font['font'] = $path;
797                 $this->generic_font['size'] = $which_size;
798                 break;
799             case 'title':
800                 $this->title_font['font'] = $path;
801                 $this->title_font['size'] = $which_size;
802                 break;
803             case 'legend':
804                 $this->legend_font['font'] = $path;
805                 $this->legend_font['size'] = $which_size;
806                 break;
807             case 'x_label':
808                 $this->x_label_font['font'] = $path;
809                 $this->x_label_font['size'] = $which_size;
810                 break;
811             case 'y_label':
812                 $this->y_label_font['font'] = $path;
813                 $this->y_label_font['size'] = $which_size;
814                 break;                   
815             case 'x_title':
816                 $this->x_title_font['font'] = $path;
817                 $this->x_title_font['size'] = $which_size;
818                 break;
819             case 'y_title':
820                 $this->y_title_font['font'] = $path;
821                 $this->y_title_font['size'] = $which_size;
822                 break;
823             default:
824                 $this->DrawError("SetFont(): Unknown element '$which_elem' specified.");
825                 return FALSE;
826             }
827             return TRUE;
828
829         } 
830
831         // Fixed fonts:
832         if ($which_font > 5 || $which_font < 0) {
833             $this->DrawError('SetFont(): Non-TTF font size must be 1, 2, 3, 4 or 5');
834             return FALSE;
835         }
836
837         switch ($which_elem) {
838         case 'generic':
839             $this->generic_font['font'] = $which_font;
840             $this->generic_font['height'] = ImageFontHeight($which_font);
841             $this->generic_font['width'] = ImageFontWidth($which_font);
842             break;
843         case 'title':
844            $this->title_font['font'] = $which_font;
845            $this->title_font['height'] = ImageFontHeight($which_font);
846            $this->title_font['width'] = ImageFontWidth($which_font);
847            break;
848         case 'legend':
849             $this->legend_font['font'] = $which_font;
850             $this->legend_font['height'] = ImageFontHeight($which_font)-$this->reduction;       // BW Patch
851             $this->legend_font['width'] = ImageFontWidth($which_font);
852             break;
853         case 'x_label':
854             $this->x_label_font['font'] = $which_font;
855             $this->x_label_font['height'] = ImageFontHeight($which_font);
856             $this->x_label_font['width'] = ImageFontWidth($which_font);
857             break;
858         case 'y_label':
859             $this->y_label_font['font'] = $which_font;
860             $this->y_label_font['height'] = ImageFontHeight($which_font);
861             $this->y_label_font['width'] = ImageFontWidth($which_font);
862             break;               
863         case 'x_title':
864             $this->x_title_font['font'] = $which_font;
865             $this->x_title_font['height'] = ImageFontHeight($which_font);
866             $this->x_title_font['width'] = ImageFontWidth($which_font);
867             break;
868         case 'y_title':
869             $this->y_title_font['font'] = $which_font;
870             $this->y_title_font['height'] = ImageFontHeight($which_font);
871             $this->y_title_font['width'] = ImageFontWidth($which_font);
872             break;
873         default:
874             $this->DrawError("SetFont(): Unknown element '$which_elem' specified.");
875             return FALSE;
876         }
877         return TRUE;
878     }
879
880
881     /*!
882      * Returns an array with the size of the bounding box of an
883      * arbitrarily placed (rotated) TrueType text string.
884      */
885     function TTFBBoxSize($size, $angle, $font, $string) 
886     {
887         // First, assume angle < 90
888         $arr = ImageTTFBBox($size, 0, $font, $string);
889         $flat_width  = $arr[2] - $arr[0];
890         $flat_height = abs($arr[3] - $arr[5]);
891
892         // Now the bounding box
893         $angle = deg2rad($angle);
894         $width  = ceil(abs($flat_width*cos($angle) + $flat_height*sin($angle))); //Must be integer
895         $height = ceil(abs($flat_width*sin($angle) + $flat_height*cos($angle))); //Must be integer
896
897         return array($width, $height);
898     }
899
900
901     /*!
902      * Draws a string of text. Horizontal and vertical alignment are relative to 
903      * to the drawing. That is: vertical text (90 deg) gets centered along y-axis 
904      * with v_align = 'center', and adjusted to the left of x-axis with h_align = 'right',
905      * 
906      * \note Original multiple lines code submitted by Remi Ricard.
907      * \note Original vertical code submitted by Marlin Viss.
908      */
909     function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text,
910                       $which_halign = 'left', $which_valign = 'bottom') 
911     {
912         // TTF:
913         if ($this->use_ttf) { 
914             $size = $this->TTFBBoxSize($which_font['size'], $which_angle, $which_font['font'], $which_text); 
915             $rads = deg2rad($which_angle);
916
917             if ($which_valign == 'center')
918                 $which_ypos += $size[1]/2;
919
920             if ($which_valign == 'bottom')
921                 $which_ypos += $size[1];
922
923             if ($which_halign == 'center')
924                 $which_xpos -= ($size[0]/2) * cos($rads);
925
926             if ($which_halign == 'left')
927                 $which_xpos += $size[0] * sin($rads);
928
929             if ($which_halign == 'right')
930                 $which_xpos -= $size[0] * cos($rads);
931
932             ImageTTFText($this->img, $which_font['size'], $which_angle, 
933                          $which_xpos, $which_ypos, $which_color, $which_font['font'], $which_text); 
934         }
935         // Fixed fonts:
936         else { 
937             // Split the text by its lines, and count them
938             $which_text = ereg_replace("\r", "", $which_text);
939             $str = split("\n", $which_text);
940             $nlines = count($str);
941             $spacing = $this->line_spacing * ($nlines - 1);
942
943             // Vertical text:
944             // (Remember the alignment convention with vertical text)
945             if ($which_angle == 90) {
946                 // The text goes around $which_xpos.
947                 if ($which_halign == 'center') 
948                     $which_xpos -= ($nlines * ($which_font['height'] + $spacing))/2;
949
950                 // Left alignment requires no modification to $xpos...
951                 // Right-align it. $which_xpos designated the rightmost x coordinate.
952                 else if ($which_halign == 'right')
953                     $which_xpos += ($nlines * ($which_font['height'] + $spacing));
954
955                 $ypos = $which_ypos;
956                 for($i = 0; $i < $nlines; $i++) { 
957                     // Center the text vertically around $which_ypos (each line)
958                     if ($which_valign == 'center')
959                         $ypos = $which_ypos + (strlen($str[$i]) * $which_font['width']) / 2;
960                     // Make the text finish (vertically) at $which_ypos
961                     if ($which_valign == 'bottom')
962                         $ypos = $which_ypos + strlen($str[$i]) * $which_font['width'];
963
964                     ImageStringUp($this->img, $which_font['font'],
965                                   $i * ($which_font['height'] + $spacing) + $which_xpos,
966                                   $ypos, $str[$i], $which_color);
967                 } 
968             } 
969             // Horizontal text:
970             else {
971                 // The text goes above $which_ypos
972                 if ($which_valign == 'top')
973                     $which_ypos -= $nlines * ($which_font['height'] + $spacing);
974                 // The text is centered around $which_ypos
975                 if ($which_valign == 'center')
976                     $which_ypos -= ($nlines * ($which_font['height'] + $spacing))/2;
977                 // valign = 'bottom' requires no modification
978
979                 $xpos = $which_xpos;
980                 for($i = 0; $i < $nlines; $i++) { 
981                     // center the text around $which_xpos
982                     if ($which_halign == 'center')
983                         $xpos = $which_xpos - (strlen($str[$i]) * $which_font['width'])/2;
984                     // make the text finish at $which_xpos
985                     if ($which_halign == 'right')
986                         $xpos = $which_xpos - strlen($str[$i]) * $which_font['width'];
987
988                     ImageString($this->img, $which_font['font'], $xpos, 
989                                 $i * ($which_font['height'] + $spacing) + $which_ypos,
990                                 $str[$i], $which_color);
991                 }                 
992             }
993         } 
994         return TRUE; 
995     } // function DrawText()
996
997
998 /////////////////////////////////////////////
999 ///////////            INPUT / OUTPUT CONTROL
1000 /////////////////////////////////////////////
1001
1002     /*!
1003      * Sets output format.
1004      * inline condition, cacheable, etc.
1005      */
1006     function SetFileFormat($which_file_format) 
1007     {
1008         // I know rewriting this was unnecessary, but it didn't work for me, I don't
1009         // understand why. 
1010         $asked = strtolower($which_file_format);
1011         switch ($asked) {
1012         case 'jpg':
1013             if (imagetypes() & IMG_JPG)
1014                 return TRUE;
1015             break;
1016         case 'png':
1017             if (imagetypes() & IMG_PNG)
1018                 return TRUE;
1019             break;
1020         case 'gif':
1021             if (imagetypes() & IMG_GIF)
1022                 return TRUE;
1023             break;
1024         case 'wbmp':
1025             if (imagetypes() & IMG_WBMP)
1026                 return TRUE;
1027             break;
1028         default:
1029             $this->PrintError("SetFileFormat(): Unrecognized option '$which_file_format'");
1030             return FALSE;
1031         }
1032         $this->PrintError("SetFileFormat():File format '$which_file_format' not supported");
1033         return FALSE;
1034     }    
1035
1036
1037     /*!
1038      * Selects an input file to be used as background for the whole graph.
1039      */
1040     function SetInputFile($which_input_file) 
1041     { 
1042         $size = GetImageSize($which_input_file);
1043         $input_type = $size[2]; 
1044
1045         switch($input_type) {
1046         case 1:
1047             $im = @ImageCreateFromGIF ($which_input_file);
1048             if (!$im) { // See if it failed 
1049                 $this->PrintError("Unable to open $which_input_file as a GIF");
1050                 return FALSE;
1051             }
1052         break;
1053         case 3:
1054             $im = @ImageCreateFromPNG ($which_input_file); 
1055             if (!$im) { // See if it failed 
1056                 $this->PrintError("Unable to open $which_input_file as a PNG");
1057                 return FALSE;
1058             }
1059         break;
1060         case 2:
1061             $im = @ImageCreateFromJPEG ($which_input_file); 
1062             if (!$im) { // See if it failed 
1063                 $this->PrintError("Unable to open $which_input_file as a JPG");
1064                 return FALSE;
1065             }
1066         break;
1067         default:
1068             $this->PrintError('SetInputFile(): Please select gif, jpg, or png for image type!');
1069             return FALSE;
1070         break;
1071         }
1072
1073         // Set Width and Height of Image
1074         $this->image_width = $size[0];
1075         $this->image_height = $size[1];
1076
1077         // Deallocate any resources previously allocated
1078         if ($this->img)
1079             imagedestroy($this->img);
1080
1081         $this->img = $im;
1082
1083         return TRUE;
1084
1085     }
1086
1087     function SetOutputFile($which_output_file) 
1088     { 
1089         $this->output_file = $which_output_file;
1090         return TRUE;
1091     }
1092
1093     /*!
1094      * Sets the output image as 'inline', ie. no Content-Type headers are sent
1095      * to the browser. Very useful if you want to embed the images.
1096      */
1097     function SetIsInline($which_ii) 
1098     {
1099         $this->is_inline = $which_ii;
1100         return TRUE;
1101     }
1102
1103
1104     /*!
1105      * Performs the actual outputting of the generated graph, and
1106      * destroys the image resource.
1107      */
1108     function PrintImage() 
1109     {
1110         // Browser cache stuff submitted by Thiemo Nagel
1111         if ( (! $this->browser_cache) && (! $this->is_inline)) {
1112             header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
1113             header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
1114             header('Cache-Control: no-cache, must-revalidate');
1115             header('Pragma: no-cache');
1116         }
1117
1118         switch($this->file_format) {
1119         case 'png':
1120             if (! $this->is_inline) {
1121                 Header('Content-type: image/png');
1122             }
1123             if ($this->is_inline && $this->output_file != '') {
1124                 ImagePng($this->img, $this->output_file);
1125             } else {
1126                 ImagePng($this->img);
1127             }
1128             break;
1129         case 'jpg':
1130             if (! $this->is_inline) {
1131                 Header('Content-type: image/jpeg');
1132             }
1133             if ($this->is_inline && $this->output_file != '') {
1134                 ImageJPEG($this->img, $this->output_file);
1135             } else {
1136                 ImageJPEG($this->img);
1137             }
1138             break;
1139         case 'gif':
1140             if (! $this->is_inline) {
1141                 Header('Content-type: image/gif');
1142             }
1143             if ($this->is_inline && $this->output_file != '') {
1144                 ImageGIF($this->img, $this->output_file);
1145             } else {
1146                 ImageGIF($this->img);
1147             }
1148
1149             break;
1150         case 'wbmp':        // wireless bitmap, 2 bit.
1151             if (! $this->is_inline) {
1152                 Header('Content-type: image/wbmp');
1153             }
1154             if ($this->is_inline && $this->output_file != '') {
1155                 ImageWBMP($this->img, $this->output_file);
1156             } else {
1157                 ImageWBMP($this->img);
1158             }
1159
1160             break;
1161         default:
1162             $this->PrintError('PrintImage(): Please select an image type!');
1163             break;
1164         }
1165         return TRUE;
1166     }
1167
1168     /*! 
1169      * Prints an error message to stdout and dies 
1170      */
1171     function PrintError($error_message) 
1172     {
1173         echo "<p><b>Fatal error</b>: $error_message<p>";
1174         die;
1175     }
1176
1177     /*!
1178      * Prints an error message inline into the generated image and draws it centered
1179      * around the given coordinates (defaults to center of the image)
1180      *   \param error_message Message to be drawn
1181      *   \param where_x       X coordinate
1182      *   \param where_y       Y coordinate
1183      */
1184     function DrawError($error_message, $where_x = NULL, $where_y = NULL) 
1185     {
1186         if (! $this->img)
1187             $this->PrintError('_DrawError(): Warning, no image resource allocated. '.
1188                               'The message to be written was: '.$error_message);
1189
1190         $ypos = (! $where_y) ? $this->image_height/2 : $where_y;
1191         $xpos = (! $where_x) ? $this->image_width/2 : $where_x;
1192         ImageRectangle($this->img, 0, 0, $this->image_width, $this->image_height,
1193                        ImageColorAllocate($this->img, 255, 255, 255));
1194
1195         $this->DrawText($this->generic_font, 0, $xpos, $ypos, ImageColorAllocate($this->img, 0, 0, 0),
1196                         $error_message, 'center', 'center');
1197
1198         $this->PrintImage();
1199         exit;
1200 //        return TRUE;
1201     }
1202
1203 /////////////////////////////////////////////
1204 ///////////                            LABELS
1205 /////////////////////////////////////////////
1206
1207
1208     /*!
1209      * Sets position for X labels following data points.
1210      */
1211     function SetXDataLabelPos($which_xdlp) 
1212     {
1213         $this->x_data_label_pos = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none',
1214                                                       __FUNCTION__);
1215         if ($which_xdlp != 'none')
1216             $this->x_tick_label_pos == 'none';
1217
1218         return TRUE;
1219     }
1220
1221     /*!
1222      * Sets position for Y labels following data points.
1223      */
1224     function SetYDataLabelPos($which_ydlp) 
1225     {
1226         $this->y_data_label_pos = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, yaxis, all, none',
1227                                                       __FUNCTION__);
1228         if ($which_ydlp != 'none')
1229             $this->y_tick_label_pos == 'none';
1230
1231         return TRUE;
1232     }
1233
1234
1235     /*!
1236      * Sets position for X labels following ticks (hence grid lines)
1237      */
1238     function SetXTickLabelPos($which_xtlp) 
1239     {
1240         $this->x_tick_label_pos = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none',
1241                                                       __FUNCTION__);
1242         if ($which_xtlp != 'none')
1243             $this->x_data_label_pos == 'none';
1244
1245         return TRUE;
1246     }
1247
1248     /*!
1249      * Sets position for Y labels following ticks (hence grid lines)
1250      */
1251     function SetYTickLabelPos($which_ytlp) 
1252     {
1253         $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none',
1254                                                       __FUNCTION__);
1255         if ($which_ytlp != 'none')
1256             $this->y_data_label_pos == 'none';
1257
1258         return TRUE;
1259     }
1260
1261     /*!
1262      * Sets type for tick and data labels on X axis.
1263      * \note 'title' type left for backwards compatibility.
1264      */
1265     function SetXLabelType($which_xlt) 
1266     {
1267         $this->x_label_type = $this->CheckOption($which_xlt, 'data, time, title', __FUNCTION__);
1268         return TRUE;
1269     }
1270
1271     /*!
1272      * Sets type for tick and data labels on Y axis.
1273      */
1274     function SetYLabelType($which_ylt) 
1275     {
1276         $this->y_label_type = $this->CheckOption($which_ylt, 'data, time', __FUNCTION__);
1277         return TRUE;
1278     }
1279
1280     function SetXTimeFormat($which_xtf) 
1281     {
1282         $this->x_time_format = $which_xtf;
1283         return TRUE;
1284     }
1285     function SetYTimeFormat($which_ytf) 
1286     {
1287         $this->y_time_format = $which_ytf;
1288         return TRUE;
1289     }
1290
1291     function SetXLabelAngle($which_xla) 
1292     {
1293         $this->x_label_angle = $which_xla;
1294         return TRUE;
1295     }
1296
1297     function SetYLabelAngle($which_yla)
1298     {
1299         $this->y_label_angle = $which_yla;
1300         return TRUE;
1301     }
1302
1303 /////////////////////////////////////////////
1304 ///////////                              MISC
1305 /////////////////////////////////////////////
1306
1307     /*!
1308      * ON THE WORKS:        XXX XXX XXX XXX
1309      *
1310      *****************                       __FUNCTION__ needs PHP 4.3.0
1311      *
1312      * Checks the valididy of an option.
1313      *  \param which_opt  String to check.
1314      *  \param which_acc  String of accepted choices.
1315      *  \param which_func Name of the calling function, for error messages.
1316      *  \note If checking everywhere for correctness slows things down, we could provide a
1317      *        child class overriding every Set...() method which uses CheckOption(). Those new
1318      *        methods could proceed in the unsafe but faster way.
1319      */
1320     function CheckOption($which_opt, $which_acc, $which_func)
1321     {
1322         $asked = trim($which_opt);
1323
1324         // FIXME: this for backward compatibility, as eregi() fails with empty strings.
1325         if ($asked == '')
1326             return '';
1327
1328         if (@ eregi($asked, $which_acc)) {
1329             return $asked;
1330         } else {
1331             $this->DrawError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
1332             return NULL;
1333         } 
1334     }
1335
1336
1337     /*!
1338      *  \note Submitted by Thiemo Nagel
1339      */
1340     function SetBrowserCache($which_browser_cache) 
1341     {
1342         $this->browser_cache = $which_browser_cache;
1343         return TRUE;
1344     }
1345
1346     /*!
1347      * Whether to show the final image or not
1348      */
1349     function SetPrintImage($which_pi) 
1350     {
1351         $this->print_image = $which_pi;
1352         return TRUE;
1353     }
1354
1355     /*!
1356      * Sets the graph's legend. If argument is not an array, appends it to the legend.
1357      */
1358     function SetLegend($which_leg)
1359     {
1360         if (is_array($which_leg)) {             // use array
1361             $this->legend = $which_leg;
1362             return TRUE;
1363         } else if (! is_null($which_leg)) {     // append string
1364             $this->legend[] = $which_leg;
1365             return TRUE;
1366         } else {
1367             $this->DrawError("SetLegend(): argument must not be null.");
1368             return FALSE;
1369         }
1370     }
1371
1372     /*!
1373      * Specifies the absolute (relative to image's up/left corner) position
1374      * of the legend's upper/leftmost corner.
1375      *  $which_type not yet used (TODO)
1376      */
1377     function SetLegendPixels($which_x, $which_y, $which_type=NULL) 
1378     { 
1379         $this->legend_x_pos = $which_x;
1380         $this->legend_y_pos = $which_y;
1381
1382         return TRUE;
1383     }
1384
1385     /*!
1386      * Specifies the relative (to graph's origin) position of the legend's
1387      * upper/leftmost corner. MUST be called after scales are set up.
1388      *   $which_type not yet used (TODO)
1389      */
1390     function SetLegendWorld($which_x, $which_y, $which_type=NULL) 
1391     { 
1392         if (! $this->scale_is_set) 
1393             $this->CalcTranslation();
1394
1395         $this->legend_x_pos = $this->xtr($which_x);
1396         $this->legend_y_pos = $this->ytr($which_y);
1397
1398         return TRUE;
1399     }
1400
1401     /*!
1402      * Accepted values are: left, sides, none, full
1403      */
1404     function SetPlotBorderType($pbt) 
1405     {
1406         $this->plot_border_type = $this->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__);
1407     }
1408
1409     /*!
1410      * Accepted values are: raised, plain
1411      */
1412     function SetImageBorderType($sibt) 
1413     {
1414         $this->image_border_type = $this->CheckOption($sibt, 'raised, plain', __FUNCTION__);
1415     }
1416
1417
1418     /*!
1419      * \param dpab bool
1420      */
1421     function SetDrawPlotAreaBackground($dpab) 
1422     {
1423         $this->draw_plot_area_background = (bool)$dpab;
1424     }
1425
1426
1427     /*!
1428      * \param dyg bool 
1429      */
1430     function SetDrawYGrid($dyg) 
1431     {
1432         $this->draw_y_grid = (bool)$dyg;
1433         return TRUE;
1434     }
1435
1436
1437     /*!
1438      * \param dxg bool
1439      */
1440     function SetDrawXGrid($dxg) 
1441     {
1442         $this->draw_x_grid = (bool)$dxg;
1443         return TRUE;
1444     }
1445
1446
1447     /*!
1448      * \param ddg bool 
1449      */
1450     function SetDrawDashedGrid($ddg) 
1451     {
1452         $this->dashed_grid = (bool)$ddg;
1453         return TRUE;
1454     }
1455
1456
1457     /*!
1458      * \param dxdl bool
1459      */
1460     function SetDrawXDataLabelLines($dxdl)
1461     {
1462         $this->draw_x_data_label_lines = (bool)$dxdl;
1463         return TRUE;
1464     }
1465
1466     
1467     /*!
1468      * TODO: draw_y_data_label_lines not implemented.
1469      * \param dydl bool
1470      */
1471     function SetDrawYDataLabelLines($dydl)
1472     {
1473         $this->draw_y_data_label_lines = $dydl;
1474         return TRUE;
1475     }
1476     /*!
1477      * Sets the graph's title.
1478      */
1479     function SetTitle($which_title) 
1480     {
1481         $this->title_txt = $which_title;
1482
1483         if ($which_title == '') {
1484             $this->title_height = 0;
1485             return TRUE;
1486         }            
1487
1488         $str = split("\n", $which_title);
1489         $lines = count($str);
1490         $spacing = $this->line_spacing * ($lines - 1);
1491
1492         if ($this->use_ttf) {
1493             $size = $this->TTFBBoxSize($this->title_font['size'], 0, $this->title_font['font'], $which_title);
1494             $this->title_height = $size[1] * $lines;
1495         } else {
1496             $this->title_height = ($this->title_font['height'] + $spacing) * $lines;
1497         }   
1498         return TRUE;
1499     }
1500
1501     /*!
1502      * Sets the X axis title and position.
1503      */
1504     function SetXTitle($which_xtitle, $which_xpos = 'plotdown') 
1505     {
1506         if ($which_xtitle == '')
1507             $which_xpos = 'none';
1508
1509         $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__);
1510
1511         $this->x_title_txt = $which_xtitle;
1512
1513         $str = split("\n", $which_xtitle);
1514         $lines = count($str);
1515         $spacing = $this->line_spacing * ($lines - 1);
1516
1517         if ($this->use_ttf) {
1518             $size = $this->TTFBBoxSize($this->x_title_font['size'], 0, $this->x_title_font['font'], $which_xtitle);
1519             $this->x_title_height = $size[1] * $lines;
1520         } else {
1521             $this->x_title_height = ($this->y_title_font['height'] + $spacing) * $lines;
1522         }
1523
1524         return TRUE;
1525     }
1526
1527
1528     /*!
1529      * Sets the Y axis title and position.
1530      */
1531     function SetYTitle($which_ytitle, $which_ypos = 'plotleft') 
1532     {
1533         if ($which_ytitle == '')
1534             $which_ypos = 'none';
1535
1536         $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__);
1537
1538         $this->y_title_txt = $which_ytitle;
1539
1540         $str = split("\n", $which_ytitle);
1541         $lines = count($str);
1542         $spacing = $this->line_spacing * ($lines - 1);
1543
1544         if ($this->use_ttf) {
1545             $size = $this->TTFBBoxSize($this->y_title_font['size'], 90, $this->y_title_font['font'], 
1546                                        $which_ytitle);
1547             $this->y_title_width = $size[0] * $lines;
1548         } else {
1549             $this->y_title_width = ($this->y_title_font['height'] + $spacing) * $lines;
1550         }
1551
1552         return TRUE;
1553     }
1554
1555     /*!
1556      * Sets the size of the drop shadow for bar and pie charts.
1557      * \param which_s int Size in pixels.
1558      */
1559     function SetShading($which_s) 
1560     { 
1561         $this->shading = (int)$which_s;
1562         return TRUE;
1563     }
1564
1565     function SetPlotType($which_pt) 
1566     {
1567         $this->plot_type = $this->CheckOption($which_pt, 
1568                                   'bars, lines, linepoints, area, points, pie, thinbarline, squared', 
1569                                   __FUNCTION__);
1570     }
1571
1572     /*!
1573      * Sets the position of Y axis.
1574      * \param pos int Position in world coordinates. 
1575      */
1576     function SetYAxisPosition($pos) 
1577     {
1578         $this->y_axis_position = (int)$pos;
1579         if (isset($this->scale_is_set)) {
1580             $this->CalcTranslation();
1581         }
1582         return TRUE;
1583     }
1584     
1585     /*!
1586      * Sets the position of X axis.
1587      * \param pos int Position in world coordinates. 
1588      */
1589     function SetXAxisPosition($pos) 
1590     {
1591         $this->x_axis_position = (int)$pos;
1592         if (isset($this->scale_is_set)) {
1593             $this->CalcTranslation();
1594         }
1595         return TRUE;
1596     }
1597
1598
1599     function SetXScaleType($which_xst) 
1600     { 
1601         $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__);
1602         return TRUE;
1603     }
1604
1605     function SetYScaleType($which_yst) 
1606     { 
1607         $this->yscale_type = $this->CheckOption($which_yst, 'linear, log',  __FUNCTION__);
1608         return TRUE;
1609     }
1610
1611     function SetPrecisionX($which_prec) 
1612     {
1613         $this->x_precision = $which_prec;
1614         return TRUE;
1615     }
1616     function SetPrecisionY($which_prec) 
1617     {
1618         $this->y_precision = $which_prec;
1619         return TRUE;
1620     }
1621
1622     function SetErrorBarLineWidth($which_seblw) 
1623     {
1624         $this->error_bar_line_width = $which_seblw;
1625         return TRUE;
1626     }
1627
1628     function SetLabelScalePosition($which_blp) 
1629     {
1630         //0 to 1
1631         $this->label_scale_position = $which_blp;
1632         return TRUE;
1633     }
1634
1635     function SetErrorBarSize($which_ebs) 
1636     {
1637         //in pixels
1638         $this->error_bar_size = $which_ebs;
1639         return TRUE;
1640     }
1641
1642     /*!
1643      * Can be one of: 'tee', 'line'
1644      */
1645     function SetErrorBarShape($which_ebs) 
1646     {
1647         $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__);
1648     }
1649
1650     /*!
1651      * Can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
1652      * 'diamond', 'triangle', 'trianglemid'
1653      */
1654     function SetPointShape($which_pt) 
1655     {
1656         $this->point_shape = $this->CheckOption($which_pt, 
1657                               'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid',
1658                               __FUNCTION__);
1659     }
1660
1661     /*!
1662      * Sets the point size for point plots.
1663      * \param ps int Size in pixels.
1664      */
1665     function SetPointSize($ps) 
1666     {
1667         $this->point_size = (int)$ps;
1668
1669         if ($this->point_shape == 'diamond' or $this->point_shape == 'triangle') {
1670             if ($this->point_size % 2 != 0) {
1671                 $this->point_size++;
1672             }
1673         }
1674         return TRUE;
1675     }
1676
1677
1678     /*!
1679      * Tells not to draw lines for missing Y data. Only works with 'lines' and 'squared' plots.
1680      * \param bl bool
1681      */
1682     function SetDrawBrokenLines($bl)
1683     {
1684         $this->draw_broken_lines = (bool)$bl;
1685     }
1686
1687
1688     /*!
1689      *  text-data: ('label', y1, y2, y3, ...)
1690      *  text-data-pie: ('label', y1), for pie charts. See DrawPieChart()
1691      *  data-data: ('label', x, y1, y2, y3, ...)
1692      *  data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...)
1693      */
1694     function SetDataType($which_dt) 
1695     {
1696         //The next three lines are for past compatibility.
1697         if ($which_dt == 'text-linear') { $which_dt = 'text-data'; };
1698         if ($which_dt == 'linear-linear') { $which_dt = 'data-data'; };
1699         if ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; };
1700
1701         $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-pie, data-data, data-data-error',
1702                                               __FUNCTION__);
1703         return TRUE;
1704     }
1705
1706     /*!
1707      * Copy the array passed as data values. We convert to numerical indexes, for its
1708      * use for (or while) loops, which sometimes are faster. Performance improvements
1709      * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions.
1710      */
1711     function SetDataValues(&$which_dv) 
1712     {
1713         $this->num_data_rows = count($which_dv);
1714         $this->total_records = 0;               // Perform some useful calculations.
1715         $this->records_per_group = 1;           
1716         for ($i = 0, $recs = 0; $i < $this->num_data_rows; $i++) {
1717             // Copy
1718             $this->data[$i] = array_values($which_dv[$i]);   // convert to numerical indices.
1719
1720             // Compute some values
1721             $recs = count($this->data[$i]); 
1722             $this->total_records += $recs;
1723
1724             if ($recs > $this->records_per_group)
1725                 $this->records_per_group = $recs;
1726
1727             $this->num_recs[$i] = $recs;
1728         }
1729     }
1730
1731     /*!
1732      * Pad styles arrays for later use by plot drawing functions:
1733      * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors
1734      * in DrawBars(), DrawLines(), etc.
1735      */
1736     function PadArrays()
1737     {
1738         array_pad_array($this->line_widths, $this->records_per_group);
1739         array_pad_array($this->line_styles, $this->records_per_group);
1740
1741         array_pad_array($this->data_colors, $this->records_per_group);
1742         array_pad_array($this->data_border_colors, $this->records_per_group); 
1743         array_pad_array($this->error_bar_colors, $this->records_per_group);
1744
1745         $this->SetDataColors();
1746         $this->SetDataBorderColors();
1747         $this->SetErrorBarColors();
1748
1749         return TRUE;
1750     }
1751
1752
1753 //////////////////////////////////////////////////////////
1754 ///////////         DATA ANALYSIS, SCALING AND TRANSLATION
1755 //////////////////////////////////////////////////////////
1756
1757     /*!
1758      * Analizes data and sets up internal maxima and minima
1759      * Needed by: CalcMargins(), ...
1760      *   Text-Data is different than data-data graphs. For them what
1761      *   we have, instead of X values, is # of records equally spaced on data.
1762      *   text-data is passed in as $data[] = (title, y1, y2, y3, y4, ...)
1763      *   data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...) 
1764      */
1765     function FindDataLimits() 
1766     {
1767         // Set some default min and max values before running through the data
1768         switch ($this->data_type) {
1769         case 'text-data':
1770             $minx = 0;
1771             $maxx = $this->num_data_rows - 1 ;
1772             $miny = $this->data[0][1];
1773             $maxy = $miny;
1774             break;
1775         default:  //Everything else: data-data, etc, take first value
1776             $minx = $this->data[0][1];
1777             $maxx = $minx;
1778             $miny = $this->data[0][2];
1779             $maxy = $miny;
1780             break;
1781         }
1782         
1783         $mine = 0;  // Maximum value for the -error bar (assume error bars always > 0) 
1784         $maxe = 0;  // Maximum value for the +error bar (assume error bars always > 0) 
1785         $maxt = 0;  // Maximum number of characters in text labels
1786         
1787         $minminy = $miny;
1788         $maxmaxy = $maxy;
1789         // Process each row of data
1790         for ($i=0; $i < $this->num_data_rows; $i++) {
1791             $j=0;
1792             // Extract maximum text label length
1793             $val = @ strlen($this->data[$i][$j++]);
1794             $maxt = ($val > $maxt) ? $val : $maxt;
1795
1796             switch ($this->data_type) {
1797             case 'text-data':           // Data is passed in as (title, y1, y2, y3, ...)
1798             case 'text-data-pie':       // This one is for some pie charts, see DrawPieChart()
1799                 // $numrecs = @ count($this->data[$i]);
1800                 $miny = $maxy = (double)$this->data[$i][$j];
1801                 for (; $j < $this->num_recs[$i]; $j++) {
1802                     $val = (double)$this->data[$i][$j];
1803                     $maxy = ($val > $maxy) ? $val : $maxy;
1804                     $miny = ($val < $miny) ? $val : $miny;
1805                 }
1806                 break;
1807             case 'data-data':           // Data is passed in as (title, x, y, y2, y3, ...) 
1808                 // X value:
1809                 $val = (double)$this->data[$i][$j++];
1810                 $maxx = ($val > $maxx) ? $val : $maxx;
1811                 $minx = ($val < $minx) ? $val : $minx;
1812                 
1813                 $miny = $maxy = (double)$this->data[$i][$j];
1814                 // $numrecs = @ count($this->data[$i]);
1815                 for (; $j < $this->num_recs[$i]; $j++) {
1816                     $val = (double)$this->data[$i][$j];
1817                     $maxy = ($val > $maxy) ? $val : $maxy;
1818                     $miny = ($val < $miny) ? $val : $miny;
1819                 }
1820                 break;
1821             case 'data-data-error':     // Data is passed in as (title, x, y, err+, err-, y2, err2+, err2-,...)
1822                 // X value:
1823                 $val = (double)$this->data[$i][$j++];
1824                 $maxx = ($val > $maxx) ? $val : $maxx;
1825                 $minx = ($val < $minx) ? $val : $minx;
1826
1827                 $miny = $maxy = (double)$this->data[$i][$j];
1828                 // $numrecs = @ count($this->data[$i]);
1829                 for (; $j < $this->num_recs[$i];) {
1830                     // Y value:
1831                     $val = (double)$this->data[$i][$j++];
1832                     $maxy = ($val > $maxy) ? $val : $maxy;
1833                     $miny = ($val < $miny) ? $val : $miny;
1834                     // Error +:
1835                     $val = (double)$this->data[$i][$j++];
1836                     $maxe = ($val > $maxe) ? $val : $maxe;
1837                     // Error -:
1838                     $val = (double)$this->data[$i][$j++];
1839                     $mine = ($val > $mine) ? $val : $mine;
1840                 }
1841                 $maxy = $maxy + $maxe;
1842                 $miny = $miny - $mine;      // assume error bars are always > 0
1843                 break;
1844             default:
1845                 $this->PrintError("FindDataLimits(): Unknown data type '$data_type'.");
1846             break;
1847             }
1848             $this->data[$i][MINY] = $miny;      // This row's min Y, for DrawXDataLine()
1849             $this->data[$i][MAXY] = $maxy;      // This row's max Y, for DrawXDataLine()
1850             $minminy = ($miny < $minminy) ? $miny : $minminy;   // global min
1851             $maxmaxy = ($maxy > $maxmaxy) ? $maxy : $maxmaxy;   // global max
1852         }
1853
1854         $this->min_x = $minx;
1855         $this->max_x = $maxx;
1856         $this->min_y = $minminy;
1857         $this->max_y = $maxmaxy;
1858         $this->max_t = $maxt;
1859
1860         $this->data_limits_done = TRUE;
1861         
1862         return TRUE;
1863     }
1864
1865
1866     /*!
1867      * Calculates image margins on the fly from title positions and sizes,
1868      * and tick labels positions and sizes.
1869      *
1870      * FIXME: fix x_data_label_pos behaviour. Now we are leaving room for it AND x_tick_label_pos
1871      *        maybe it shouldn't be so...
1872      *
1873      * TODO: add x_tick_label_width and y_tick_label_height and use them to calculate
1874      *       max_x_labels and max_y_labels, to be used by drawing functions t avoid overlapping.
1875      */
1876     function CalcMargins() 
1877     {
1878         // Temporary variables for label size calculation
1879         $xlab = $this->FormatLabel('x', $this->max_x);
1880         $ylab = $this->FormatLabel('y', $this->max_y);
1881
1882         //////// Calculate maximum X/Y axis label height and width:
1883
1884         // TTFonts:
1885         if ($this->use_ttf) {
1886             // Maximum X axis label height
1887             $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle,
1888                                        $this->x_label_font['font'], $xlab);
1889             $this->x_tick_label_height = $size[1];
1890
1891             // Maximum Y axis label width
1892             $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle, 
1893                                         $this->y_label_font['font'], $ylab);
1894             $this->y_tick_label_width = $size[0];
1895         }
1896         // Fixed fonts:
1897         else { 
1898             // Maximum X axis label height
1899             if ($this->x_label_angle == 90)
1900                 $this->x_tick_label_height = strlen($xlab) * $this->x_label_font['width'];
1901             else 
1902                 $this->x_tick_label_height = $this->x_label_font['height'];
1903
1904             // Maximum Y axis label width
1905             $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
1906         }
1907
1908
1909         ///////// Calculate margins:
1910
1911         // Upper title, ticks and tick labels, and data labels:
1912         $this->y_top_margin = $this->title_height + $this->safe_margin * 2;
1913
1914         if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both')
1915             $this->y_top_margin += $this->x_title_height + $this->safe_margin;
1916
1917         if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both')
1918             $this->y_top_margin += $this->x_tick_label_height;
1919
1920         if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both')
1921             $this->y_top_margin += $this->x_tick_length * 2;
1922
1923         if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
1924             $this->y_top_margin += $this->x_tick_label_height;
1925
1926         // Lower title, ticks and tick labels, and data labels:
1927         $this->y_bot_margin = $this->safe_margin * 2; 
1928
1929         if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both')
1930             $this->y_bot_margin += $this->x_title_height;
1931
1932         if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both')
1933             $this->y_bot_margin += $this->x_tick_length * 2;
1934
1935         if ($this->x_tick_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
1936             $this->y_bot_margin += $this->x_tick_length * 2;
1937
1938         if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both')            
1939             $this->y_bot_margin += $this->x_tick_label_height;
1940
1941         if ($this->x_tick_label_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
1942             $this->y_bot_margin += $this->x_tick_label_height;
1943
1944         if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
1945             $this->y_bot_margin += $this->x_tick_label_height;
1946
1947         // Left title, ticks and tick labels:
1948         $this->x_left_margin = $this->safe_margin * 2;
1949
1950         if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both')
1951             $this->x_left_margin += $this->y_title_width + $this->safe_margin;
1952
1953         if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both')
1954             $this->x_left_margin += $this->y_tick_label_width;
1955
1956         if ($this->y_tick_pos == 'plotleft' || $this->y_tick_pos == 'both')
1957             $this->x_left_margin += $this->y_tick_length * 2 ;
1958
1959         // Right title, ticks and tick labels:
1960         $this->x_right_margin = $this->safe_margin * 2;
1961
1962         if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both')
1963             $this->x_right_margin += $this->y_title_width + $this->safe_margin;
1964
1965         if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both')
1966             $this->x_right_margin += $this->y_tick_label_width;
1967
1968         if ($this->y_tick_pos == 'plotright' || $this->y_tick_pos == 'both')
1969             $this->x_right_margin += $this->y_tick_length * 2;
1970
1971
1972         $this->x_tot_margin = $this->x_left_margin + $this->x_right_margin;
1973         $this->y_tot_margin = $this->y_top_margin + $this->y_bot_margin;
1974
1975         return;
1976     }
1977
1978
1979     /*!
1980      * Set the margins in pixels (left, right, top, bottom)
1981      */
1982     function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm) 
1983     { 
1984
1985         $this->x_left_margin = $which_lm;
1986         $this->x_right_margin = $which_rm;
1987         $this->x_tot_margin = $which_lm + $which_rm;
1988
1989         $this->y_top_margin = $which_tm;
1990         $this->y_bot_margin = $which_bm;
1991         $this->y_tot_margin = $which_tm + $which_bm;
1992
1993         $this->SetPlotAreaPixels();
1994
1995         return;
1996     }
1997
1998
1999     /*!
2000      * Sets the limits for the plot area. If no arguments are supplied, uses
2001      * values calculated from _CalcMargins();
2002      * Like in GD, (0,0) is upper left
2003      *
2004      * This resets the scale if SetPlotAreaWorld() was already called
2005      */
2006     function SetPlotAreaPixels($x1=NULL, $y1=NULL, $x2=NULL, $y2=NULL) 
2007     {
2008         if ($x2 && $y2) {
2009             $this->plot_area = array($x1, $y1, $x2, $y2);
2010         } else {
2011             if (! isset($this->x_tot_margin))
2012                 $this->CalcMargins();
2013
2014             $this->plot_area = array($this->x_left_margin, $this->y_top_margin,
2015                                      $this->image_width - $this->x_right_margin,
2016                                      $this->image_height - $this->y_bot_margin);
2017         }
2018         $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
2019         $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
2020
2021         // Reset the scale with the new plot area.
2022         if (isset($this->plot_max_x))
2023             $this->CalcTranslation();
2024
2025         return TRUE;
2026
2027     }
2028
2029
2030     /*!
2031      * Sets minimum and maximum x and y values in the plot using FindDataLimits()
2032      * or from the supplied parameters, if any.
2033      *
2034      * This resets the scale if SetPlotAreaPixels() was already called
2035      */
2036     function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) 
2037     {
2038         if ((! $xmin)  && (! $xmax) ) {
2039             // For automatic setting of data we need data limits
2040             if (! isset($this->data_limits_done)) {
2041                 $this->FindDataLimits() ;
2042             }
2043             if ($this->data_type == 'text-data') {
2044                 $xmax = $this->max_x + 1 ;  // valid for BAR CHART TYPE GRAPHS ONLY
2045                 $xmin = 0 ;                 // valid for BAR CHART TYPE GRAPHS ONLY
2046             } else {
2047                 $xmax = $this->max_x;
2048                 $xmin = $this->min_x;
2049             }
2050
2051             $ymax = ceil($this->max_y * 1.1);
2052             if ($this->min_y < 0) {
2053                 $ymin = floor($this->min_y * 1.1);
2054             } else {
2055                 $ymin = 0;
2056             }
2057         }
2058
2059         $this->plot_min_x = $xmin;
2060         $this->plot_max_x = $xmax;
2061
2062         if ($ymin == $ymax) {
2063             $ymax += 1;
2064         }
2065         if ($this->yscale_type == 'log') { 
2066             //extra error checking
2067             if ($ymin <= 0) { 
2068                 $ymin = 1;
2069             }
2070             if ($ymax <= 0) {
2071                 $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0');
2072                 return FALSE;
2073             }
2074         }
2075
2076         $this->plot_min_y = $ymin;
2077         $this->plot_max_y = $ymax;
2078
2079         if ($ymax <= $ymin) {
2080             $this->DrawError('SetPlotAreaWorld(): Error in data - max not greater than min');
2081             return FALSE;
2082         }
2083
2084         // Reset the scale with the new maxs and mins
2085         if (isset($this->plot_area_width)) {
2086             $this->CalcTranslation();
2087         }
2088
2089         return TRUE;
2090     } //function SetPlotAreaWorld
2091
2092
2093     /*!
2094      * For plots that have equally spaced x variables and multiple bars per x-point.
2095      */
2096     function SetEqualXCoord() 
2097     {
2098         $space = ($this->plot_area[2] - $this->plot_area[0]) / 
2099                  ($this->num_data_rows * 2) * $this->group_frac_width;
2100         $group_width = $space * 2;
2101         $bar_width = $group_width / $this->records_per_group;
2102         //I think that eventually this space variable will be replaced by just graphing x.
2103         $this->data_group_space = $space;
2104         $this->record_bar_width = $bar_width;
2105         return TRUE;
2106     }
2107
2108     /*!
2109      * Calculates scaling stuff...
2110      */
2111     function CalcTranslation() 
2112     {
2113         if ($this->xscale_type == 'log') { 
2114             $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x));
2115         } else { 
2116             $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x);
2117         }
2118         if ($this->yscale_type == 'log') { 
2119             $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y));
2120         } else { 
2121             $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y);
2122         }
2123
2124         // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively
2125         if ($this->xscale_type == 'log') { 
2126             $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) );
2127         } else { 
2128             $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x);
2129         }
2130         if ($this->yscale_type == 'log') { 
2131             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y));
2132         } else { 
2133             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y);
2134         }
2135
2136         $this->scale_is_set = TRUE;
2137
2138         /************** FIXME?? *************/
2139         // There should be a better place for this.
2140
2141         // User provided y axis position?
2142         if ($this->y_axis_position != '') { 
2143             // Make sure we draw our axis inside the plot
2144             $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x) 
2145                                      ? $this->plot_min_x : $this->y_axis_position;
2146             $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x) 
2147                                      ? $this->plot_max_x : $this->y_axis_position;
2148             $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
2149         } else { 
2150             // Default to left axis
2151             $this->y_axis_x_pixels = $this->xtr($this->plot_min_x);
2152         }
2153         // User provided x axis position?
2154         if ($this->x_axis_position != '') { 
2155             // Make sure we draw our axis inside the plot
2156             $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y) 
2157                                      ? $this->plot_min_y : $this->x_axis_position;
2158             $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y) 
2159                                      ? $this->plot_max_y : $this->x_axis_position;
2160             $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
2161         } else { 
2162             if ($this->yscale_type == 'log')
2163                 $this->x_axis_y_pixels = $this->ytr(1);
2164             else 
2165                 // Default to axis at 0 or plot_min_y (should be 0 anyway, from SetPlotAreaWorld())
2166                 $this->x_axis_y_pixels = ($this->plot_min_y <= 0) && (0 <= $this->plot_max_y) 
2167                                          ? $this->ytr(0) : $this->ytr($this->plot_min_y);
2168         }
2169
2170     } // function CalcTranslation()
2171
2172
2173     /*!
2174      * Translate X world coordinate into pixel coordinate
2175      * Needs values calculated by _CalcTranslation()
2176      */
2177     function xtr($x_world) 
2178     {
2179         //$x_pixels =  $this->x_left_margin + ($this->image_width - $this->x_tot_margin)*
2180         //      (($x_world - $this->plot_min_x) / ($this->plot_max_x - $this->plot_min_x)) ;
2181         //which with a little bit of math reduces to ...
2182         if ($this->xscale_type == 'log') { 
2183             $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ;
2184         } else { 
2185             $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ;
2186         }
2187         return round($x_pixels);
2188     }
2189
2190
2191     /*!
2192      * Translate Y world coordinate into pixel coordinate.
2193      * Needs values calculated by _CalcTranslation()
2194      */
2195     function ytr($y_world) 
2196     {
2197         if ($this->yscale_type == 'log') { 
2198             //minus because GD defines y = 0 at top. doh!
2199             $y_pixels =  $this->plot_origin_y - log10($y_world) * $this->yscale ;  
2200         } else { 
2201             $y_pixels =  $this->plot_origin_y - $y_world * $this->yscale ;  
2202         }
2203         return round($y_pixels);
2204     }
2205
2206     /*!
2207      * Formats a tick or data label.
2208      *
2209      * \note Time formatting suggested by Marlin Viss
2210      */
2211     function FormatLabel($which_pos, $which_lab) 
2212     { 
2213         switch ($which_pos) {
2214         case 'x':
2215         case 'plotx':
2216             switch ($this->x_label_type) {
2217             case 'title':
2218                 $lab = $this->data[$which_lab][0];
2219                 break;
2220             case 'data':
2221                 $lab = number_format($which_lab, $this->x_precision, '.', ', ').$this->data_units_text;
2222                 break;
2223             case 'time':
2224                 $lab = strftime($this->x_time_format, $which_lab);
2225                 break;
2226             default:
2227                 // Unchanged from whatever format it is passed in
2228                 $lab = $which_lab;
2229             break;
2230             }    
2231             break;
2232         case 'y':
2233         case 'ploty':
2234             switch ($this->y_label_type) {
2235             case 'data':
2236                 $lab = number_format($which_lab, $this->y_precision, '.', ', ').$this->data_units_text;
2237                 break;
2238             case 'time':
2239                 $lab = strftime($this->y_time_format, $which_lab);
2240                 break;
2241             default:
2242                 // Unchanged from whatever format it is passed in
2243                 $lab = $which_lab;
2244                 break;
2245             }
2246             break;
2247         default:
2248             $this->PrintError("FormatLabel(): Unknown label type $which_type");
2249             return NULL;
2250         } 
2251
2252         return $lab;
2253     } //function FormatLabel
2254
2255
2256
2257 /////////////////////////////////////////////    
2258 ///////////////                         TICKS
2259 /////////////////////////////////////////////    
2260
2261     /*!
2262      * Use either this or SetNumXTicks() to set where to place x tick marks
2263      */
2264     function SetXTickIncrement($which_ti=NULL) 
2265     {
2266         if ($which_ti) {
2267             $this->x_tick_increment = $which_ti;  //world coordinates
2268         } else {
2269             if (! isset($this->data_limits_done)) {
2270                 $this->FindDataLimits();  //Get maxima and minima for scaling
2271             }
2272             //$this->x_tick_increment = ( ceil($this->max_x * 1.2) - floor($this->min_x * 1.2) )/10;
2273             $this->x_tick_increment =  ($this->plot_max_x  - $this->plot_min_x  )/10;
2274         }
2275         $this->num_x_ticks = ''; //either use num_y_ticks or y_tick_increment, not both
2276         return TRUE;
2277     }
2278
2279     /*!
2280      * Use either this or SetNumYTicks() to set where to place y tick marks
2281      */
2282     function SetYTickIncrement($which_ti=NULL) 
2283     {
2284         if ($which_ti) {
2285             $this->y_tick_increment = $which_ti;  //world coordinates
2286         } else {
2287             if (! isset($this->data_limits_done)) {
2288                 $this->FindDataLimits();  //Get maxima and minima for scaling
2289             }
2290             if (! isset($this->plot_max_y))
2291                 $this->SetPlotAreaWorld();
2292
2293             //$this->y_tick_increment = ( ceil($this->max_y * 1.2) - floor($this->min_y * 1.2) )/10;
2294             $this->y_tick_increment =  ($this->plot_max_y  - $this->plot_min_y  )/10;
2295         }
2296         $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_increment, not both
2297         return TRUE;
2298     }
2299
2300
2301     function SetNumXTicks($which_nt) 
2302     {
2303         $this->num_x_ticks = $which_nt;
2304         $this->x_tick_increment = '';  //either use num_x_ticks or x_tick_increment, not both
2305         return TRUE;
2306     }
2307
2308     function SetNumYTicks($which_nt) 
2309     {
2310         $this->num_y_ticks = $which_nt;
2311         $this->y_tick_increment = '';  //either use num_y_ticks or y_tick_increment, not both
2312         return TRUE;
2313     }
2314
2315     /*!
2316      *
2317      */
2318     function SetYTickPos($which_tp) 
2319     { 
2320         $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', __FUNCTION__);
2321         return TRUE;
2322     }
2323     /*!
2324      *
2325      */
2326     function SetXTickPos($which_tp) 
2327     { 
2328         $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__); 
2329         return TRUE;
2330     }
2331
2332     /*!
2333      * \param skip bool
2334      */ 
2335     function SetSkipTopTick($skip)
2336     {
2337         $this->skip_top_tick = (bool)$skip;
2338         return TRUE;
2339     }
2340
2341     /*!
2342      * \param skip bool
2343      */
2344     function SetSkipBottomTick($skip) 
2345     {
2346         $this->skip_bottom_tick = (bool)$skip;
2347         return TRUE;
2348     }
2349
2350     function SetXTickLength($which_xln) 
2351     {
2352         $this->x_tick_length = $which_xln;
2353         return TRUE;
2354     }
2355
2356     function SetYTickLength($which_yln) 
2357     {
2358         $this->y_tick_length = $which_yln;
2359         return TRUE;
2360     }
2361
2362     function SetXTickCrossing($which_xc) 
2363     {
2364         $this->x_tick_cross = $which_xc;
2365         return TRUE;
2366     }
2367
2368     function SetYTickCrossing($which_yc) 
2369     {
2370         $this->y_tick_cross = $which_yc;
2371         return TRUE;
2372     }
2373
2374
2375 /////////////////////////////////////////////
2376 ////////////////////          GENERIC DRAWING
2377 /////////////////////////////////////////////
2378
2379     /*!
2380      * Fills the background of the image with a solid color
2381      */
2382     function DrawBackground() 
2383     {
2384         if (! $this->background_done) {     //Don't draw it twice if drawing two plots on one image
2385             ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, 
2386                                  $this->ndx_bg_color);
2387             $this->background_done = TRUE;
2388         }
2389         return TRUE;
2390     }
2391
2392     /*!
2393      * Draws a border around the final image.
2394      */
2395     function DrawImageBorder() 
2396     {
2397         switch ($this->image_border_type) {
2398         case 'raised':
2399             ImageLine($this->img, 0, 0, $this->image_width-1, 0, $this->ndx_i_border);
2400             ImageLine($this->img, 1, 1, $this->image_width-2, 1, $this->ndx_i_border);
2401             ImageLine($this->img, 0, 0, 0, $this->image_height-1, $this->ndx_i_border);
2402             ImageLine($this->img, 1, 1, 1, $this->image_height-2, $this->ndx_i_border);
2403             ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
2404                       $this->image_height-1, $this->ndx_i_border_dark);
2405             ImageLine($this->img, 0, $this->image_height-1, $this->image_width-1,
2406                       $this->image_height-1, $this->ndx_i_border_dark);
2407             ImageLine($this->img, $this->image_width-2, 1, $this->image_width-2,
2408                       $this->image_height-2, $this->ndx_i_border_dark);
2409             ImageLine($this->img, 1, $this->image_height-2, $this->image_width-2,
2410                       $this->image_height-2, $this->ndx_i_border_dark);
2411             break;
2412         case 'plain':
2413             ImageLine($this->img, 0, 0, $this->image_width, 0, $this->ndx_i_border_dark);
2414             ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
2415                       $this->image_height, $this->ndx_i_border_dark);
2416             ImageLine($this->img, $this->image_width-1, $this->image_height-1, 0, $this->image_height-1,
2417                       $this->ndx_i_border_dark);
2418             ImageLine($this->img, 0, 0, 0, $this->image_height, $this->ndx_i_border_dark);
2419             break;
2420         case 'none':
2421             break;
2422         default:
2423             $this->DrawError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'");
2424             return FALSE;
2425         }
2426         return TRUE;
2427     }
2428
2429
2430     /*!
2431      * Adds the title to the graph.
2432      */
2433     function DrawTitle() 
2434     {
2435         // Center of the plot area
2436         //$xpos = ($this->plot_area[0] + $this->plot_area_width )/ 2;
2437
2438         // Center of the image:
2439         $xpos = $this->image_width / 2;
2440
2441         // Place it at almost at the top
2442         $ypos = $this->safe_margin;
2443
2444         $this->DrawText($this->title_font, $this->title_angle, $xpos, $ypos,
2445                         $this->ndx_title_color, $this->title_txt, 'center', 'bottom'); 
2446
2447         return TRUE; 
2448
2449     }
2450
2451
2452     /*!
2453      * Draws the X-Axis Title
2454      */
2455     function DrawXTitle() 
2456     {
2457         if ($this->x_title_pos == 'none')
2458             return;
2459
2460         // Center of the plot
2461         $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2;
2462
2463         // Upper title
2464         if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') {
2465             $ypos = $this->safe_margin + $this->title_height + $this->safe_margin;
2466             $this->DrawText($this->x_title_font, $this->x_title_angle,
2467                             $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
2468         }
2469         // Lower title
2470         if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') {
2471             $ypos = $this->image_height - $this->x_title_height - $this->safe_margin;
2472             $this->DrawText($this->x_title_font, $this->x_title_angle,
2473                             $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
2474         } 
2475         return TRUE;
2476     }
2477
2478     /*!
2479      * Draws the Y-Axis Title
2480      */
2481     function DrawYTitle() 
2482     {
2483         if ($this->y_title_pos == 'none')
2484             return;
2485
2486         // Center the title vertically to the plot
2487         $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2;
2488
2489         if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') {
2490             $xpos = $this->safe_margin;
2491             $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, 
2492                             $this->y_title_txt, 'left', 'center');
2493         }
2494         if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') {
2495             $xpos = $this->image_width - $this->safe_margin - $this->y_title_width - $this->safe_margin;
2496             $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color, 
2497                             $this->y_title_txt, 'left', 'center');
2498         }
2499
2500         return TRUE;
2501     }
2502
2503
2504     /*!
2505      * Fills the plot area with a solid color
2506      */
2507     function DrawPlotAreaBackground() 
2508     {
2509         ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1], 
2510                              $this->plot_area[2], $this->plot_area[3],
2511                              $this->ndx_plot_bg_color);
2512         return TRUE;
2513     }
2514
2515     /*
2516      * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
2517      */
2518     function DrawYAxis() 
2519     {
2520         // Draw ticks, labels and grid, if any
2521         $this->DrawYTicks();
2522
2523         // Draw Y axis at X = y_axis_x_pixels
2524         ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1], 
2525                   $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color);
2526                   
2527         return TRUE;
2528     }
2529
2530     /*
2531      *
2532      */
2533     function DrawXAxis() 
2534     {
2535         // Draw ticks, labels and grid
2536         $this->DrawXTicks();
2537
2538         //Draw Tick and Label for Y axis
2539         if (! $this->skip_bottom_tick) { 
2540             $ylab =$this->FormatLabel('y', $this->x_axis_position);
2541             $this->DrawYTick($ylab, $this->x_axis_y_pixels);
2542         }
2543
2544         //Draw X Axis at Y = x_axis_y_pixels
2545         ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels,
2546                   $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color);
2547
2548         return TRUE;
2549     }
2550
2551     /*!
2552      * Draw Just one Tick, called from DrawYTicks() and DrawXAxis()
2553      * TODO? Move this inside DrawYTicks() and Modify DrawXAxis() ?
2554      */
2555     function DrawYTick($which_ylab, $which_ypix) 
2556     {
2557         // Ticks on Y axis 
2558         if ($this->y_tick_pos == 'yaxis') { 
2559             ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix, 
2560                       $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, 
2561                       $this->ndx_tick_color);
2562         }
2563
2564         // Labels on Y axis
2565         if ($this->y_tick_label_pos == 'yaxis') {
2566             $this->DrawText($this->y_label_font, $this->y_label_angle,
2567                             $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix, 
2568                             $this->ndx_text_color, $which_ylab, 'right', 'center');
2569         }
2570
2571         // Ticks to the left of the Plot Area
2572         if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) { 
2573             ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length,
2574                       $which_ypix, $this->plot_area[0] + $this->y_tick_cross,
2575                       $which_ypix, $this->ndx_tick_color);
2576         }
2577
2578         // Ticks to the right of the Plot Area
2579         if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) { 
2580             ImageLine($this->img, ($this->plot_area[2] + $this->y_tick_length),
2581                       $which_ypix, $this->plot_area[2] - $this->y_tick_cross,
2582                       $which_ypix, $this->ndx_tick_color);
2583         }
2584
2585         // Labels to the left of the plot area
2586         if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') {
2587             $this->DrawText($this->y_label_font, $this->y_label_angle, 
2588                             $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix, 
2589                             $this->ndx_text_color, $which_ylab, 'right', 'center');
2590         }
2591         // Labels to the right of the plot area
2592         if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') {
2593             $this->DrawText($this->y_label_font, $this->y_label_angle, 
2594                             $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix, 
2595                             $this->ndx_text_color, $which_ylab, 'left', 'center');
2596         }
2597    } // Function DrawYTick()
2598
2599
2600     /*!
2601      * Draws Grid, Ticks and Tick Labels along Y-Axis
2602      * Ticks and ticklabels can be left of plot only, right of plot only, 
2603      * both on the left and right of plot, or crossing a user defined Y-axis
2604      */
2605     function DrawYTicks() 
2606     {
2607         // Sets the line style for IMG_COLOR_STYLED lines (grid)
2608         if ($this->dashed_grid) {
2609             $this->SetDashedStyle($this->ndx_light_grid_color);
2610             $style = IMG_COLOR_STYLED;
2611         } else {
2612             $style = $this->ndx_light_grid_color;
2613         }
2614
2615         // maxy is always > miny so delta_y is always positive
2616         if ($this->y_tick_increment) {
2617             $delta_y = $this->y_tick_increment;
2618         } elseif ($this->num_y_ticks) {
2619             $delta_y = ($this->plot_max_y - $this->plot_min_y) / $this->num_y_ticks;
2620         } else {
2621             $delta_y = ($this->plot_max_y - $this->plot_min_y) / 10 ;
2622         }
2623
2624         // NOTE: When working with floats, because of approximations when adding $delta_y, 
2625         // $y_tmp never equals $y_end  at the for loop, so one spurious line would  get drawn where 
2626         // not for the substraction to $y_end here.
2627         $y_tmp = (double)$this->plot_min_y;
2628         $y_end = (double)$this->plot_max_y - ($delta_y/2);
2629
2630         if ($this->skip_bottom_tick)
2631             $y_tmp += $delta_y;
2632
2633         if ($this->skip_top_tick)
2634             $y_end -= $delta_y;
2635
2636         for (;$y_tmp < $y_end; $y_tmp += $delta_y) {
2637             $ylab = $this->FormatLabel('y', $y_tmp);
2638             $y_pixels = $this->ytr($y_tmp);
2639
2640             // Horizontal grid line
2641             if ($this->draw_y_grid) {
2642                 ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1, $y_pixels, $style);
2643             }
2644
2645             // Draw ticks
2646             $this->DrawYTick($ylab, $y_pixels);
2647         }
2648         return TRUE;
2649     } // function DrawYTicks
2650
2651
2652     /*!
2653      * Draws Grid, Ticks and Tick Labels along X-Axis
2654      * Ticks and tick labels can be down of plot only, up of plot only, 
2655      * both on up and down of plot, or crossing a user defined X-axis 
2656      *
2657      * \note Original vertical code submitted by Marlin Viss
2658      */
2659     function DrawXTicks() 
2660     {
2661         // Sets the line style for IMG_COLOR_STYLED lines (grid)
2662         if ($this->dashed_grid) {
2663             $this->SetDashedStyle($this->ndx_light_grid_color);
2664             $style = IMG_COLOR_STYLED;
2665         } else {
2666             $style = $this->ndx_light_grid_color;
2667         }
2668
2669         // Calculate x increment between ticks
2670         if ($this->x_tick_increment) {
2671             $delta_x = $this->x_tick_increment;
2672         } elseif ($this->num_x_ticks) {
2673             $delta_x = ($this->plot_max_x - $this->plot_min_x) / $this->num_x_ticks;
2674         } else {
2675             $delta_x =($this->plot_max_x - $this->plot_min_x) / 10 ;
2676         }
2677
2678         // NOTE: When working with decimals, because of approximations when adding $delta_x, 
2679         // $x_tmp never equals $x_end  at the for loop, so one spurious line would  get drawn where 
2680         // not for the substraction to $x_end here.
2681         $x_tmp = (double)$this->plot_min_x;
2682         $x_end = (double)$this->plot_max_x - ($delta_x/2);
2683
2684         // Should the leftmost tick be drawn?
2685         if ($this->skip_left_tick)
2686             $x_tmp += $delta_x;
2687
2688         // And the rightmost?
2689         if (! $this->skip_right_tick)
2690             $x_end += $delta_x;
2691
2692         for (;$x_tmp < $x_end; $x_tmp += $delta_x) { 
2693             $xlab = $this->FormatLabel('x', $x_tmp);
2694             $x_pixels = $this->xtr($x_tmp);
2695
2696             // Vertical grid lines
2697             if ($this->draw_x_grid) {
2698                 ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style);
2699             }
2700
2701             // Tick on X Axis 
2702             if ($this->x_tick_pos == 'xaxis') { 
2703                 ImageLine($this->img, $x_pixels, $this->x_axis_y_pixels - $this->x_tick_cross, 
2704                           $x_pixels, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color);
2705             }
2706
2707             // Label on X axis
2708             if ($this->x_tick_label_pos == 'xaxis') {
2709                  $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, 
2710                                 $this->x_axis_y_pixels + $this->x_tick_length*1.5, $this->ndx_text_color, 
2711                                 $xlab, 'center', 'bottom');
2712             }              
2713
2714             // Top of the plot area tick
2715             if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') {
2716                 ImageLine($this->img, $x_pixels, $this->plot_area[1] - $this->x_tick_length,
2717                           $x_pixels, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color);
2718             }
2719             // Bottom of the plot area tick
2720             if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') {
2721                 ImageLine($this->img, $x_pixels, $this->plot_area[3] + $this->x_tick_length,
2722                           $x_pixels, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color);
2723             }
2724
2725             // Top of the plot area tick label
2726             if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') {
2727                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, 
2728                                 $this->plot_area[1] - $this->x_tick_length*1.5, $this->ndx_text_color, 
2729                                 $xlab, 'center', 'top');
2730             }
2731
2732             // Bottom of the plot area tick label
2733             if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') {
2734                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels, 
2735                                 $this->plot_area[3] + $this->x_tick_length*1.5, $this->ndx_text_color, 
2736                                 $xlab, 'center', 'bottom');
2737             }
2738         }
2739         return;
2740     } // function DrawXTicks
2741
2742
2743     /*!
2744      * 
2745      */
2746     function DrawPlotBorder() 
2747     {
2748         switch ($this->plot_border_type) {
2749         case 'left':    // for past compatibility
2750         case 'plotleft':
2751             ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
2752                       $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
2753             break;
2754         case 'right':
2755         case 'plotright':
2756             ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
2757                       $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
2758             break;
2759         case 'both':
2760         case 'sides':
2761              ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
2762                       $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
2763             ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
2764                       $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
2765             break;
2766         case 'none':
2767             //Draw No Border
2768             break;
2769         case 'full':
2770         default:
2771             ImageRectangle($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
2772                            $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
2773             break;
2774         }
2775         return TRUE;
2776     }
2777
2778
2779     /*!
2780      * Draws the data label associated with a point in the plot.
2781      * This is different from x_labels drawn by DrawXTicks() and care
2782      * should be taken not to draw both, as they'd probably overlap.
2783      * Calling of this function in DrawLines(), etc is decided after x_data_label_pos value.
2784      * Leave the last parameter out, to avoid the drawing of vertical lines, no matter
2785      * what the setting is (for plots that need it, like DrawSquared())
2786      */
2787     function DrawXDataLabel($xlab, $xpos, $row=FALSE) 
2788     {
2789         $xlab = $this->FormatLabel('x', $xlab);
2790    
2791         // Labels below the plot area
2792         if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
2793             $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, 
2794                             $this->plot_area[3] + $this->x_tick_length, 
2795                             $this->ndx_text_color, $xlab, 'center', 'bottom');
2796         
2797         // Labels above the plot area
2798         if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
2799             $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, 
2800                             $this->plot_area[1] - $this->x_tick_length , 
2801                             $this->ndx_text_color, $xlab, 'center', 'top');
2802
2803         if ($row && $this->draw_x_data_label_lines)
2804             $this->DrawXDataLine($xpos, $row);
2805     }
2806     
2807     /*!
2808      * Draws Vertical lines from data points up and down.
2809      * Which lines are drawn depends on the value of x_data_label_pos,
2810      * and whether this is at all done or not, on draw_x_data_label_lines
2811      *
2812      * \param xpos int position in pixels of the line.
2813      * \param row int index of the data row being drawn.
2814      */
2815     function DrawXDataLine($xpos, $row) 
2816     {
2817         // Sets the line style for IMG_COLOR_STYLED lines (grid)
2818         if($this->dashed_grid) {
2819             $this->SetDashedStyle($this->ndx_light_grid_color);
2820             $style = IMG_COLOR_STYLED;
2821         } else {
2822             $style = $this->ndx_light_grid_color;
2823         }
2824
2825         // Lines from the bottom up
2826         if ($this->x_data_label_pos == 'both') {
2827             ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style);
2828         }         
2829         // Lines coming from the bottom of the plot
2830         else if ($this->x_data_label_pos == 'plotdown') {
2831             // See FindDataLimits() to see why 'MAXY' index.
2832             $ypos = $this->ytr($this->data[$row][MAXY]);
2833             ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style);
2834         }
2835         // Lines coming from the top of the plot
2836         else if ($this->x_data_label_pos == 'plotup') {
2837             // See FindDataLimits() to see why 'MINY' index.
2838             $ypos = $this->ytr($this->data[$row][MINY]);
2839             ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style);
2840         }
2841     } 
2842     
2843 /*    
2844     function DrawPlotLabel($xlab, $xpos, $ypos) 
2845     {
2846         $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, $this
2847 */
2848
2849     /*!
2850      * Draws the graph legend
2851      *
2852      * \note Base code submitted by Marlin Viss
2853      * FIXME: maximum label length should be calculated more accurately for TT fonts
2854      *        Performing a BBox calculation for every legend element, for example.
2855      */
2856     function DrawLegend($which_x1, $which_y1, $which_boxtype) 
2857     {
2858         // Find maximum legend label length
2859         $max_len = 0;
2860         foreach ($this->legend as $leg) {
2861             $len = strlen($leg);
2862             $max_len = ($len > $max_len) ? $len : $max_len;
2863         }
2864         $max_len += 5;          // Leave room for the boxes and margins
2865
2866         /////// Calculate legend labels sizes:  FIXME - dirty hack - FIXME
2867         // TTF:
2868         if ($this->use_ttf) {
2869             $size = $this->TTFBBoxSize($this->legend_font['size'], 0,
2870                                        $this->legend_font['font'], '_');
2871             $char_w = $size[0];
2872
2873             $size = $this->TTFBBoxSize($this->legend_font['size'], 0,
2874                                        $this->legend_font['font'], '|');
2875             $char_h = $size[1];                                       
2876         } 
2877         // Fixed fonts:
2878         else {
2879             $char_w = $this->legend_font['width'];
2880             $char_h = $this->legend_font['height'];
2881         }
2882
2883         $v_margin = $char_h/2;                         // Between vertical borders and labels
2884         $dot_height = $char_h + $this->line_spacing;   // Height of the small colored boxes
2885         $width = $char_w * $max_len;
2886
2887         //////// Calculate box size
2888         // upper Left
2889         if ( (! $which_x1) || (! $which_y1) ) {
2890             $box_start_x = $this->plot_area[2] - $width;
2891             $box_start_y = $this->plot_area[1] + 5;
2892         } else { 
2893             $box_start_x = $which_x1;
2894             $box_start_y = $which_y1;
2895         }
2896
2897         // Lower right corner
2898         $box_end_y = $box_start_y + $dot_height*(count($this->legend)) + 2*$v_margin; 
2899         $box_end_x = $box_start_x + $width - 5;
2900
2901
2902         // Draw outer box
2903         ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_bg_color);
2904         ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_grid_color);
2905
2906         $color_index = 0;
2907         $max_color_index = count($this->ndx_data_colors) - 1;
2908
2909         $dot_left_x = $box_end_x - $char_w * 2;
2910         $dot_right_x = $box_end_x - $char_w;
2911         $y_pos = $box_start_y + $v_margin;
2912
2913         foreach ($this->legend as $leg) {
2914             // Text right aligned to the little box
2915             $this->DrawText($this->legend_font, 0, $dot_left_x - $char_w, $y_pos, 
2916                             $this->ndx_text_color, $leg, 'right');
2917             // Draw a box in the data color
2918             ImageFilledRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x,
2919                                  $y_pos + $dot_height-1, $this->ndx_data_colors[$color_index]);
2920             // Draw a rectangle around the box
2921             ImageRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x,
2922                            $y_pos + $dot_height-1, $this->ndx_text_color);
2923
2924             $y_pos += $char_h + $this->line_spacing;
2925
2926             $color_index++;
2927             if ($color_index > $max_color_index) 
2928                 $color_index = 0;
2929         }
2930     } // Function DrawLegend()
2931
2932
2933     /*!
2934      * TODO Draws a legend over (or below) an axis of the plot.
2935      */
2936     function DrawAxisLegend()
2937     {
2938         // Calculate available room
2939         // Calculate length of all items (boxes included)
2940         // Calculate number of lines and room it would take. FIXME: this should be known in CalcMargins()
2941         // Draw.
2942     }
2943
2944 /////////////////////////////////////////////
2945 ////////////////////             PLOT DRAWING
2946 /////////////////////////////////////////////
2947
2948
2949     /*!
2950      * Draws a pie chart. Data has to be 'text-data' type.
2951      * 
2952      *  This can work in two ways: the classical, with a column for each sector 
2953      *  (computes the column totals and draws the pie with that) 
2954      *  OR
2955      *  Takes each row as a sector and uses it's first value. This has the added
2956      *  advantage of using the labels provided, which is not the case with the
2957      *  former method. This might prove useful for pie charts from GROUP BY sql queries
2958      */
2959     function DrawPieChart() 
2960     {
2961         $xpos = $this->plot_area[0] + $this->plot_area_width/2;
2962         $ypos = $this->plot_area[1] + $this->plot_area_height/2;
2963         $diameter = min($this->plot_area_width, $this->plot_area_height);
2964         $radius = $diameter/2;
2965
2966         // Get sum of each column? One pie slice per column
2967         if ($this->data_type === 'text-data') {
2968             for ($i = 0; $i < $this->num_data_rows; $i++) {
2969                 for ($j = 1; $j < $this->num_recs[$i]; $j++) {      // Label ($row[0]) unused in these pie charts
2970                     @ $sumarr[$j] += abs($this->data[$i][$j]);      // NOTE!  sum > 0 to make pie charts
2971                 }
2972             }
2973         }
2974         // Or only one column per row, one pie slice per row?
2975         else if ($this->data_type == 'text-data-pie') {
2976             for ($i = 0; $i < $this->num_data_rows; $i++) {
2977                 $legend[$i] = $this->data[$i][0];                   // Set the legend to column labels
2978                 $sumarr[$i] = $this->data[$i][1];
2979             }
2980         }
2981         else if ($this->data_type == 'data-data') {
2982             for ($i = 0; $i < $this->num_data_rows; $i++) {
2983                 for ($j = 2; $j < $this->num_recs[$i]; $j++) {
2984                     @ $sumarr[$j] += abs($this->data[$i][$j]);
2985                 }
2986             }
2987         }            
2988         else {
2989             $this->DrawError("DrawPieChart(): Data type '$this->data_type' not supported.");
2990             return FALSE;
2991         }
2992
2993         $total = array_sum($sumarr);
2994
2995         if ($total == 0) {
2996             $this->DrawError('DrawPieChart(): Empty data set');
2997             return FALSE;
2998         }
2999
3000         if ($this->shading) {
3001             $diam2 = $diameter / 2;
3002         } else {
3003             $diam2 = $diameter;
3004         }
3005         $max_data_colors = count ($this->data_colors);
3006
3007         for ($h = $this->shading; $h >= 0; $h--) {
3008             $color_index = 0;
3009             $start_angle = 0;
3010             $end_angle = 0;
3011             foreach ($sumarr as $val) {
3012                 // For shaded pies: the last one (at the top of the "stack") has a brighter color:
3013                 if ($h == 0)
3014                     $slicecol = $this->ndx_data_colors[$color_index];
3015                 else                
3016                     $slicecol = $this->ndx_data_dark_colors[$color_index];
3017
3018                 $label_txt = number_format(($val / $total * 100), $this->y_precision, '.', ', ') . '%';
3019                 $val = 360 * ($val / $total);
3020
3021                 // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why),
3022                 // so the pie chart would start clockwise from 3 o'clock, would it not be
3023                 // for the reversal of start and end angles in imagefilledarc()
3024                 $start_angle = $end_angle;
3025                 $end_angle += $val;
3026                 $mid_angle = deg2rad($end_angle - ($val / 2));
3027
3028                 // Draw the slice       
3029                 ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, 
3030                                360-$end_angle, 360-$start_angle,
3031                                $slicecol, IMG_ARC_PIE);
3032
3033                 // Draw the labels only once
3034                 if ($h == 0) {
3035                     // Draw the outline
3036                     if (! $this->shading)
3037                         ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, 
3038                                        360-$end_angle, 360-$start_angle,
3039                                        $this->ndx_grid_color, IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL);
3040
3041
3042                     // The '* 1.2' trick is to get labels out of the pie chart so there are more 
3043                     // chances they can be seen in small sectors.
3044                     $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position;
3045                     $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position;
3046
3047                     $this->DrawText($this->generic_font, 0, $label_x, $label_y, $this->ndx_grid_color,
3048                                     $label_txt, 'center', 'center');           
3049                 }                                
3050                 $color_index++;
3051                 $color_index = $color_index % $max_data_colors;
3052             }   // end for
3053         }   // end for
3054     }
3055
3056
3057     /*!
3058      * Supported data formats: data-data-error, text-data-error (doesn't exist yet)
3059      * ( data comes in as array("title", x, y, error+, error-, y2, error2+, error2-, ...) )
3060      */
3061     function DrawDotsError() 
3062     {
3063         $this->CheckOption($this->data_type, 'data-data-error', __FUNCTION__);
3064
3065         for($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3066             $record = 1;                                // Skip record #0 (title)
3067
3068             // Do we have a value for X?
3069             if ($this->data_type == 'data-data-error')
3070                 $x_now = $this->data[$row][$record++];  // Read it, advance record index
3071             else
3072                 $x_now = 0.5 + $cnt++;                  // Place text-data at X = 0.5, 1.5, 2.5, etc...
3073                 
3074             // Draw X Data labels?
3075             if ($this->x_data_label_pos != 'none')
3076                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
3077
3078             while ($record < $this->num_recs[$row]) {
3079                     // Y:
3080                     $y_now = $this->data[$row][$record++];
3081                     $this->DrawDot($x_now, $y_now, $this->point_shape, $this->ndx_data_colors[$record]);
3082
3083                     // Error +
3084                     $val = $this->data[$row][$record++];
3085                     $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
3086                                          $this->ndx_error_bar_colors[$record]);
3087                     // Error -
3088                     $val = $this->data[$row][$record];
3089                     $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
3090                                          $this->ndx_error_bar_colors[$record++]);
3091             }
3092         }
3093     } // function DrawDotsError()
3094
3095
3096     /*
3097      * Supported data types: 
3098      *  - data-data ("title", x, y1, y2, y3, ...)
3099      *  - text-data ("title", y1, y2, y3, ...)
3100      */
3101     function DrawDots() 
3102     {
3103         $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
3104
3105         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3106             $rec = 1;                    // Skip record #0 (data label)
3107
3108             // Do we have a value for X?
3109             if ($this->data_type == 'data-data')
3110                 $x_now = $this->data[$row][$rec++];  // Read it, advance record index
3111             else
3112                 $x_now = 0.5 + $cnt++;       // Place text-data at X = 0.5, 1.5, 2.5, etc...
3113
3114             $x_now_pixels = $this->xtr($x_now);
3115
3116             // Draw X Data labels?
3117             if ($this->x_data_label_pos != 'none')
3118                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
3119
3120             // Proceed with Y values
3121             for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
3122                 if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data 
3123                     $this->DrawDot($x_now, $this->data[$row][$rec], 
3124                                    $this->point_shape, $this->ndx_data_colors[$idx]);
3125                 }
3126             }
3127         }
3128     } //function DrawDots
3129
3130
3131     /*!
3132      * A clean, fast routine for when you just want charts like stock volume charts
3133      */
3134     function DrawThinBarLines() 
3135     {
3136         $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
3137
3138         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3139             $rec = 1;                    // Skip record #0 (data label)
3140
3141             // Do we have a value for X?
3142             if ($this->data_type == 'data-data')
3143                 $x_now = $this->data[$row][$rec++];  // Read it, advance record index
3144             else
3145                 $x_now = 0.5 + $cnt++;       // Place text-data at X = 0.5, 1.5, 2.5, etc...
3146
3147             $x_now_pixels = $this->xtr($x_now);
3148
3149             // Draw X Data labels?
3150             if ($this->x_data_label_pos != 'none')
3151                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
3152
3153             // Proceed with Y values
3154             for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
3155                 if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data 
3156                     ImageSetThickness($this->img, $this->line_widths[$idx]);
3157                     // Draws a line from user defined x axis position up to ytr($val)
3158                     ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, $x_now_pixels, 
3159                               $this->ytr($this->data[$row][$rec]), $this->ndx_data_colors[$idx]);
3160                 }                              
3161             }
3162         }
3163
3164         ImageSetThickness($this->img, 1);
3165     }  //function DrawThinBarLines
3166
3167     /*!
3168      *
3169      */
3170     function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color) 
3171     {
3172         /* 
3173         // TODO: add a parameter to show datalabels next to error bars?
3174         // something like this:
3175         if ($this->x_data_label_pos == 'plot') {
3176             $this->DrawText($this->error_font, 90, $x1, $y2, 
3177                             $color, $label, 'center', 'top');
3178         */
3179
3180         $x1 = $this->xtr($x_world);
3181         $y1 = $this->ytr($y_world);
3182         $y2 = $this->ytr($y_world+$error_height) ;
3183
3184         ImageSetThickness($this->img, $this->error_bar_line_width);
3185         ImageLine($this->img, $x1, $y1 , $x1, $y2, $color);
3186
3187         switch ($error_bar_type) {
3188         case 'line':
3189             break;
3190         case 'tee':
3191             ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color);
3192             break;
3193         default:
3194             ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color);
3195             break;
3196         }
3197
3198         ImageSetThickness($this->img, 1);
3199         return TRUE;
3200     }
3201
3202     /*!
3203      * Draws a styled dot. Uses world coordinates.
3204      * Supported types: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
3205      * 'diamond', 'triangle', 'trianglemid'
3206      */
3207     function DrawDot($x_world, $y_world, $dot_type, $color) 
3208     {
3209         $half_point = $this->point_size / 2;
3210
3211         $x_mid = $this->xtr($x_world);
3212         $y_mid = $this->ytr($y_world);
3213
3214         $x1 = $x_mid - $half_point;
3215         $x2 = $x_mid + $half_point;
3216         $y1 = $y_mid - $half_point;
3217         $y2 = $y_mid + $half_point;
3218
3219         switch ($dot_type) {
3220         case 'halfline':
3221             ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color);
3222             break;
3223         case 'line':
3224             ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
3225             break;
3226         case 'plus':
3227             ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
3228             ImageLine($this->img, $x_mid, $y1, $x_mid, $y2, $color);
3229             break;
3230         case 'cross':
3231             ImageLine($this->img, $x1, $y1, $x2, $y2, $color);
3232             ImageLine($this->img, $x1, $y2, $x2, $y1, $color);
3233             break;
3234         case 'rect':
3235             ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
3236             break;
3237         case 'circle':
3238             ImageArc($this->img, $x_mid, $y_mid, $this->point_size, $this->point_size, 0, 360, $color);
3239             break;
3240         case 'dot':
3241             ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_size, $this->point_size, 0, 360, 
3242                            $color, IMG_ARC_PIE);
3243             break;
3244         case 'diamond':
3245             $arrpoints = array( $x1, $y_mid, $x_mid, $y1, $x2, $y_mid, $x_mid, $y2);
3246             ImageFilledPolygon($this->img, $arrpoints, 4, $color);
3247             break;
3248         case 'triangle':
3249             $arrpoints = array( $x1, $y_mid, $x2, $y_mid, $x_mid, $y2);
3250             ImageFilledPolygon($this->img, $arrpoints, 3, $color);
3251             break;
3252         case 'trianglemid':
3253             $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y_mid);
3254             ImageFilledPolygon($this->img, $arrpoints, 3, $color);
3255             break;
3256         default:
3257             ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
3258             break;
3259         }
3260         return TRUE;
3261     }
3262
3263     /*!
3264      * Draw an area plot. Supported data types:
3265      *      'text-data'
3266      *      'data-data'
3267      * NOTE: This function used to add first and last data values even on incomplete
3268      *       sets. That is not the behaviour now. As for missing data in between, 
3269      *       there are two posibilities: replace the point with one on the X axis (previous
3270      *       way), or forget about it and use the preceding and following ones to draw the polygon.
3271      *       There is the possibility to use both, we just need to add the method to set
3272      *       it. Something like SetMissingDataBehaviour(), for example.
3273      */
3274     function DrawArea() 
3275     {
3276         $incomplete_data_defaults_to_x_axis = FALSE;        // TODO: make this configurable
3277
3278         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3279             $rec = 1;                                       // Skip record #0 (data label)
3280
3281             if ($this->data_type == 'data-data')            // Do we have a value for X?
3282                 $x_now = $this->data[$row][$rec++];         // Read it, advance record index
3283             else
3284                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
3285
3286             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
3287
3288
3289             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
3290                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);                   
3291
3292             // Proceed with Y values
3293             // Create array of points for imagefilledpolygon()
3294             for($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) {
3295                 if (is_numeric($this->data[$row][$rec])) {              // Allow for missing Y data 
3296                     $y_now_pixels = $this->ytr($this->data[$row][$rec]);
3297
3298                     $posarr[$idx][] = $x_now_pixels;
3299                     $posarr[$idx][] = $y_now_pixels;
3300
3301                     $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1;
3302                 }
3303                 // If there's missing data...
3304                 else {
3305                     if (isset ($incomplete_data_defaults_to_x_axis)) {
3306                         $posarr[$idx][] = $x_now_pixels;
3307                         $posarr[$idx][] = $this->x_axis_y_pixels;
3308                         $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1;
3309                     }
3310                 }
3311             }
3312         }   // end for
3313
3314         $end = count($posarr);
3315         for ($i = 0; $i < $end; $i++) {
3316             // Prepend initial points. X = first point's X, Y = x_axis_y_pixels
3317             $x = $posarr[$i][0];
3318             array_unshift($posarr[$i], $x, $this->x_axis_y_pixels);
3319
3320             // Append final points. X = last point's X, Y = x_axis_y_pixels
3321             $x = $posarr[$i][count($posarr[$i])-2];
3322             array_push($posarr[$i], $x, $this->x_axis_y_pixels);
3323
3324             $num_points[$i] += 2;
3325
3326             // Draw the poligon
3327             ImageFilledPolygon($this->img, $posarr[$i], $num_points[$i], $this->ndx_data_colors[$i]);
3328         }
3329
3330     } // function DrawArea()
3331
3332
3333     /*!
3334      * Draw Lines. Supported data-types:
3335      *      'data-data', 
3336      *      'text-data'
3337      * NOTE: Please see the note regarding incomplete data sets on DrawArea()
3338      */
3339     function DrawLines() 
3340     {
3341         // This will tell us if lines have already begun to be drawn.
3342         // It is an array to keep separate information for every line, with a single
3343         // variable we would sometimes get "undefined offset" errors and no plot...
3344         $start_lines = array_fill(0, $this->records_per_group, FALSE);
3345
3346         if ($this->data_type == 'text-data') { 
3347             $lastx[0] = $this->xtr(0);
3348             $lasty[0] = $this->xtr(0);
3349         }
3350
3351         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3352             $record = 1;                                    // Skip record #0 (data label)
3353
3354             if ($this->data_type == 'data-data')            // Do we have a value for X?
3355                 $x_now = $this->data[$row][$record++];      // Read it, advance record index
3356             else
3357                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
3358
3359             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
3360
3361             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
3362                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
3363
3364             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
3365                 if (is_numeric($this->data[$row][$record])) {           //Allow for missing Y data 
3366                     $y_now_pixels = $this->ytr($this->data[$row][$record]);
3367
3368                     if ($start_lines[$idx] == TRUE) {
3369                         // Set line width, revert it to normal at the end
3370                         ImageSetThickness($this->img, $this->line_widths[$idx]);
3371
3372                         if ($this->line_styles[$idx] == 'dashed') {
3373                             $this->SetDashedStyle($this->ndx_data_colors[$idx]);
3374                             ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
3375                                       IMG_COLOR_STYLED);
3376                         } else {
3377                             ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
3378                                       $this->ndx_data_colors[$idx]);
3379                         }
3380
3381                     }
3382                     $lasty[$idx] = $y_now_pixels;
3383                     $lastx[$idx] = $x_now_pixels;
3384                     $start_lines[$idx] = TRUE;
3385                 } 
3386                 // Y data missing... should we leave a blank or not?
3387                 else if ($this->draw_broken_lines) {
3388                     $start_lines[$idx] = FALSE;
3389                 }
3390             }   // end for
3391         }   // end for
3392
3393         ImageSetThickness($this->img, 1);       // Revert to original state for lines to be drawn later. 
3394     } // function DrawLines()
3395
3396
3397     /*!
3398      * Draw lines with error bars - data comes in as 
3399      *      array("label", x, y, error+, error-, y2, error2+, error2-, ...);
3400      */
3401     function DrawLinesError() 
3402     {
3403         if ($this->data_type != 'data-data-error') {
3404             $this->DrawError("DrawLinesError(): Data type '$this->data_type' not supported.");
3405             return FALSE;
3406         }
3407
3408         $start_lines = array_fill(0, $this->records_per_group, FALSE);
3409
3410         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3411             $record = 1;                                    // Skip record #0 (data label)
3412
3413             $x_now = $this->data[$row][$record++];          // Read X value, advance record index
3414
3415             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates.
3416             
3417
3418             if ($this->x_data_label_pos != 'none')          // Draw X Data labels? 
3419                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
3420
3421             // Now go for Y, E+, E-
3422             for ($idx = 0; $record < $this->num_recs[$row]; $idx++) {
3423                 // Y
3424                 $y_now = $this->data[$row][$record++];
3425                 $y_now_pixels = $this->ytr($y_now);
3426
3427                 if ($start_lines[$idx] == TRUE) {
3428                     ImageSetThickness($this->img, $this->line_widths[$idx]);
3429
3430                     if ($this->line_styles[$idx] == 'dashed') {
3431                         $this->SetDashedStyle($this->ndx_data_colors[$idx]);
3432                         ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
3433                                   IMG_COLOR_STYLED);
3434                     } else {
3435                         ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], 
3436                                   $this->ndx_data_colors[$idx]);
3437                     }
3438                 }
3439
3440                 // Error+
3441                 $val = $this->data[$row][$record++];
3442                 $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, 
3443                                      $this->ndx_error_bar_colors[$idx]);
3444
3445                 // Error-
3446                 $val = $this->data[$row][$record++];
3447                 $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, 
3448                                      $this->ndx_error_bar_colors[$idx]);
3449
3450                 // Update indices:
3451                 $start_lines[$idx] = TRUE;   // Tells us if we already drew the first column of points, 
3452                                              // thus having $lastx and $lasty ready for the next column.
3453                 $lastx[$idx] = $x_now_pixels;
3454                 $lasty[$idx] = $y_now_pixels;
3455             }   // end while
3456         }   // end for
3457
3458         ImageSetThickness($this->img, 1);   // Revert to original state for lines to be drawn later.
3459     }   // function DrawErrorLines()
3460
3461
3462
3463     /*!
3464      * This is a mere copy of DrawLines() with one more line drawn for each point
3465      */
3466     function DrawSquared() 
3467     {
3468         // This will tell us if lines have already begun to be drawn.
3469         // It is an array to keep separate information for every line, for with a single
3470         // variable we could sometimes get "undefined offset" errors and no plot...
3471         $start_lines = array_fill(0, $this->records_per_group, FALSE);
3472
3473         if ($this->data_type == 'text-data') { 
3474             $lastx[0] = $this->xtr(0);
3475             $lasty[0] = $this->xtr(0);
3476         }
3477         
3478         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
3479             $record = 1;                                    // Skip record #0 (data label)
3480
3481             if ($this->data_type == 'data-data')            // Do we have a value for X?
3482                 $x_now = $this->data[$row][$record++];      // Read it, advance record index
3483             else
3484                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
3485
3486             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
3487
3488             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
3489                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param.
3490                 
3491             // Draw Lines
3492             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
3493                 if (is_numeric($this->data[$row][$record])) {               // Allow for missing Y data 
3494                     $y_now_pixels = $this->ytr($this->data[$row][$record]);
3495
3496                     if ($start_lines[$idx] == TRUE) {
3497                         // Set line width, revert it to normal at the end
3498                         ImageSetThickness($this->img, $this->line_widths[$idx]);
3499
3500                         if ($this->line_styles[$idx] == 'dashed') {
3501                             $this->SetDashedStyle($this->ndx_data_colors[$idx]);
3502                             ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx], 
3503                                       IMG_COLOR_STYLED);
3504                             ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels, 
3505                                       IMG_COLOR_STYLED);
3506                         } else {
3507                             ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
3508                                       $this->ndx_data_colors[$idx]);
3509                             ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels,
3510                                       $this->ndx_data_colors[$idx]);
3511                         }
3512                     }
3513                     $lastx[$idx] = $x_now_pixels;
3514                     $lasty[$idx] = $y_now_pixels;
3515                     $start_lines[$idx] = TRUE;
3516                 } 
3517                 // Y data missing... should we leave a blank or not?
3518                 else if ($this->draw_broken_lines) {
3519                     $start_lines[$idx] = FALSE;
3520                 } 
3521             }
3522         }   // end while
3523
3524         ImageSetThickness($this->img, 1); 
3525     } // function DrawSquared()
3526
3527
3528     /*!    
3529      * Data comes in as array("title", x, y, y2, y3, ...)
3530      */
3531     function DrawBars() 
3532     {
3533         if ($this->data_type != 'text-data') { 
3534             $this->DrawError('DrawBars(): Bar plots must be text-data: use function SetDataType("text-data")');
3535             return FALSE;
3536         }
3537
3538         for ($row = 0; $row < $this->num_data_rows; $row++) {
3539             $record = 1;                                    // Skip record #0 (data label)
3540
3541             $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
3542
3543             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
3544                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
3545
3546             // Draw the bar
3547             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
3548                 if (is_numeric($this->data[$row][$record])) {       // Allow for missing Y data 
3549                     $x1 = $x_now_pixels - $this->data_group_space + ($idx * $this->record_bar_width);
3550                     $x2 = $x1 + ($this->bar_width_adjust * $this->record_bar_width); 
3551
3552                     if ($this->data[$row][$record] < $this->x_axis_position) {
3553                         $y1 = $this->x_axis_y_pixels;
3554                         $y2 = $this->ytr($this->data[$row][$record]);
3555                     } else {
3556                         $y1 = $this->ytr($this->data[$row][$record]);
3557                         $y2 = $this->x_axis_y_pixels;
3558                     }
3559
3560                     if ($this->shading) {                           // Draw the shade?
3561                         ImageFilledPolygon($this->img, array($x1, $y1, 
3562                                                        $x1 + $this->shading, $y1 - $this->shading,
3563                                                        $x2 + $this->shading, $y1 - $this->shading,
3564                                                        $x2 + $this->shading, $y2 - $this->shading,
3565                                                        $x2, $y2,
3566                                                        $x2, $y1),
3567                                            6, $this->ndx_data_dark_colors[$idx]);
3568                     } 
3569                     // Or draw a border?
3570                     else {
3571                         ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]);
3572                     }
3573                     // Draw the bar
3574                     ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
3575                 } 
3576             }   // end for
3577         }   // end for
3578     } //function DrawBars    
3579
3580
3581     /*!
3582      *
3583      */
3584     function DrawGraph() 
3585     {
3586         if (! $this->img) {
3587             $this->DrawError('DrawGraph(): No image resource allocated');
3588             return FALSE;
3589         }
3590
3591         if (! is_array($this->data)) {
3592             $this->DrawError("DrawGraph(): No array of data in \$data");
3593             return FALSE;
3594         }
3595
3596         if (! isset($this->data_limits_done)) 
3597             $this->FindDataLimits();                // Get maxima and minima for scaling
3598
3599         if ($this->total_records == 0) {            // Check for empty data sets
3600             $this->DrawError('Empty data set');
3601             return FALSE;
3602         }
3603
3604         $this->CalcMargins();                       // Calculate margins
3605
3606         if (! isset($this->plot_area_width))        // Set plot area pixel values (plot_area[])
3607             $this->SetPlotAreaPixels();
3608
3609         if (! isset($this->plot_max_y))             // Set plot area world values (plot_max_x, etc.)
3610             $this->SetPlotAreaWorld();
3611
3612         if ($this->data_type == 'text-data')
3613             $this->SetEqualXCoord();
3614
3615         if ($this->x_data_label_pos != 'none') {    // Default: do not draw tick stuff if 
3616             $this->x_tick_label_pos = 'none';       // there are data labels.
3617             $this->x_tick_pos = 'none';
3618         }
3619
3620         $this->PadArrays();                         // Pad color and style arrays to fit records per group.
3621
3622         $this->DrawBackground();
3623
3624         $this->DrawImageBorder();
3625
3626         if ($this->draw_plot_area_background)
3627             $this->DrawPlotAreaBackground();
3628
3629         $this->DrawTitle();
3630         $this->DrawXTitle();
3631         $this->DrawYTitle();
3632
3633         switch ($this->plot_type) {
3634         case 'thinbarline':
3635             $this->DrawThinBarLines();
3636             break;
3637         case 'area':
3638             $this->DrawArea();
3639             break;
3640         case 'squared':
3641             $this->DrawSquared();
3642             break;
3643         case 'lines':
3644             if ( $this->data_type == 'data-data-error') {
3645                 $this->DrawLinesError();
3646             } else {
3647                 $this->DrawLines();
3648             }
3649             break;
3650         case 'linepoints':          // FIXME !!! DrawXDataLabel gets called in DrawLines() and DrawDots()
3651             if ( $this->data_type == 'data-data-error') {
3652                 $this->DrawLinesError();
3653                 $this->DrawDotsError();
3654             } else {
3655                 $this->DrawLines();
3656                 $this->DrawDots();
3657             }
3658             break;
3659         case 'points';
3660             if ( $this->data_type == 'data-data-error') {
3661                 $this->DrawDotsError();
3662             } else {
3663                 $this->DrawDots();
3664             }
3665             break;
3666         case 'pie':
3667             // Pie charts can maximize image space usage.
3668             $this->SetPlotAreaPixels($this->safe_margin, $this->title_height,
3669                                      $this->image_width - $this->safe_margin,
3670                                      $this->image_height - $this->safe_margin);
3671             $this->DrawPieChart();
3672             break;
3673         case 'bars':
3674         default:
3675             $this->plot_type = 'bars';  // Set it if it wasn't already set.
3676             $this->DrawYAxis();     // We don't want the grid to overwrite bar charts
3677             $this->DrawXAxis();     // so we draw it first. Also, Y must be drawn before X (see DrawYAxis())
3678             $this->DrawBars();
3679             $this->DrawPlotBorder();
3680             break;
3681         }   // end switch
3682
3683         if ($this->plot_type != 'pie' && $this->plot_type != 'bars') {
3684             $this->DrawYAxis();
3685             $this->DrawXAxis();
3686             $this->DrawPlotBorder();
3687         }
3688         if ($this->legend)
3689             $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
3690
3691         if ($this->print_image)
3692             $this->PrintImage();
3693
3694     } //function DrawGraph()
3695
3696 /////////////////////////////////////////////
3697 //////////////////         DEPRECATED METHODS
3698 /////////////////////////////////////////////
3699
3700     /*!
3701      * Deprecated, use SetYTickPos()
3702      */
3703     function SetDrawVertTicks($which_dvt) 
3704     {
3705         if ($which_dvt != 1)
3706             $this->SetYTickPos('none');
3707         return TRUE;
3708     } 
3709
3710     /*!
3711      * Deprecated, use SetXTickPos()
3712      */
3713     function SetDrawHorizTicks($which_dht) 
3714     {
3715         if ($which_dht != 1)
3716            $this->SetXTickPos('none');
3717         return TRUE;
3718     }
3719
3720     /*!
3721      * \deprecated Use SetNumXTicks()
3722      */
3723     function SetNumHorizTicks($n) 
3724     {
3725         return $this->SetNumXTicks($n);
3726     }
3727
3728     /*!
3729      * \deprecated Use SetNumYTicks()
3730      */
3731     function SetNumVertTicks($n) 
3732     {
3733         return $this->SetNumYTicks($n);
3734     }
3735
3736     /*!
3737      * \deprecated Use SetXTickIncrement()
3738      */
3739     function SetHorizTickIncrement($inc) 
3740     {
3741         return $this->SetXTickIncrement($inc);
3742     }
3743
3744
3745     /*!
3746      * \deprecated Use SetYTickIncrement()
3747      */
3748     function SetVertTickIncrement($inc) 
3749     {
3750         return $this->SetYTickIncrement($inc);
3751     }
3752
3753     /*!
3754      * \deprecated Use SetYTickPos()
3755      */
3756     function SetVertTickPosition($which_tp) 
3757     { 
3758         return $this->SetYTickPos($which_tp); 
3759     }
3760
3761     /*!
3762      * \deprecated Use SetXTickPos()
3763      */
3764     function SetHorizTickPosition($which_tp) 
3765     { 
3766         return $this->SetXTickPos($which_tp); 
3767     }
3768
3769     /*!
3770      * \deprecated Use SetFont()
3771      */
3772     function SetTitleFontSize($which_size) 
3773     {
3774         return $this->SetFont('title', $which_size);
3775     }
3776
3777     /*!
3778      * \deprecated Use SetFont()
3779      */
3780     function SetAxisFontSize($which_size) 
3781     {
3782         $this->SetFont('x_label', $which_size);
3783         $this->SetFont('y_label', $whic_size);
3784     }
3785
3786     /*!
3787      * \deprecated Use SetFont()
3788      */
3789     function SetSmallFontSize($which_size) 
3790     {
3791         return $this->SetFont('generic', $which_size);
3792     }
3793
3794     /*!
3795      * \deprecated Use SetFont()
3796      */
3797     function SetXLabelFontSize($which_size) 
3798     {
3799         return $this->SetFont('x_title', $which_size);
3800     }
3801
3802     /*!
3803      * \deprecated Use SetFont()
3804      */
3805     function SetYLabelFontSize($which_size) 
3806     {
3807         return $this->SetFont('y_title', $which_size);
3808     }
3809
3810     /*!
3811      * \deprecated Use SetXTitle()
3812      */ 
3813     function SetXLabel($which_xlab) 
3814     {
3815         return $this->SetXTitle($which_xlab);
3816     }
3817
3818     /*!
3819      * \deprecated Use SetYTitle()
3820      */ 
3821     function SetYLabel($which_ylab) 
3822     {
3823         return $this->SetYTitle($which_ylab);
3824     }   
3825
3826     /*!
3827      * \deprecated This is now an Internal function - please set width and 
3828      *             height via PHPlot() upon object construction
3829      */
3830     function SetImageArea($which_iw, $which_ih) 
3831     {
3832         $this->image_width = $which_iw;
3833         $this->image_height = $which_ih;
3834
3835         return TRUE;
3836     }
3837
3838     /*!
3839      * \deprecated Use SetXTickLength() and SetYTickLength() instead.
3840      */
3841     function SetTickLength($which_tl) 
3842     {
3843         $this->SetXTickLength($which_tl);
3844         $this->SetYTickLength($which_tl);
3845         return TRUE;
3846     }
3847
3848     /*!
3849      * \deprecated  Use SetYLabelType()
3850      */
3851     function SetYGridLabelType($which_yglt) 
3852     {
3853         return $this->SetYLabelType($which_yglt);
3854     }
3855
3856     /*!
3857      * \deprecated  Use SetXLabelType()
3858      */
3859     function SetXGridLabelType($which_xglt) 
3860     {
3861         return $this->SetXLabelType($which_xglt);
3862     }
3863     /*!
3864      * \deprecated Use SetYTickLabelPos()
3865      */
3866     function SetYGridLabelPos($which_yglp) 
3867     {
3868         return $this->SetYTickLabelPos($which_yglp);
3869     }
3870     /*!
3871      * \deprecated Use SetXTickLabelPos()
3872      */
3873     function SetXGridLabelPos($which_xglp) 
3874     {
3875         return $this->SetXTickLabelPos($which_xglp);
3876     }
3877
3878
3879     /*!
3880      * \deprecated Use SetXtitle()
3881      */
3882     function SetXTitlePos($xpos) 
3883     {
3884         $this->x_title_pos = $xpos;
3885         return TRUE;
3886     }
3887
3888     /*!
3889      * \deprecated Use SetYTitle()
3890      */
3891     function SetYTitlePos($xpos) 
3892     {
3893         $this->y_title_pos = $xpos;
3894         return TRUE;
3895     }
3896
3897     /*!
3898      * \deprecated  Use DrawDots()
3899      */
3900     function DrawDotSeries() 
3901     {
3902         $this->DrawDots();
3903     }
3904
3905     /*!
3906      * \deprecated Use SetXLabelAngle()
3907      */
3908     function SetXDataLabelAngle($which_xdla) 
3909     {
3910         return $this->SetXLabelAngle($which_xdla);
3911     }
3912
3913     /*!
3914      * Draw Labels (not grid labels) on X Axis, following data points. Default position is 
3915      * down of plot. Care must be taken not to draw these and x_tick_labels as they'd probably overlap.
3916      *
3917      * \deprecated Use SetXDataLabelPos()
3918      */
3919     function SetDrawXDataLabels($which_dxdl) 
3920     {
3921         if ($which_dxdl == '1' )
3922             $this->SetXDataLabelPos('plotdown');
3923         else
3924             $this->SetXDataLabelPos('none');
3925     }
3926
3927     /*!
3928      * \deprecated This method was intended to improve performance by being specially 
3929      * written for 'data-data'. However, the improvement didn't pay. Use DrawLines() instead
3930      */
3931     function DrawLineSeries() 
3932     {
3933         return $this->DrawLines();
3934     }
3935
3936     /*!
3937      * \deprecated Calculates maximum X-Axis label height. Now inside _CalcMargins()
3938      */
3939     function CalcXHeights() 
3940     {
3941         // TTF
3942         if ($this->use_ttf) {
3943             $xstr = str_repeat('.', $this->max_t);
3944             $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle,
3945                                        $this->x_label_font['font'], $xstr);
3946             $this->x_tick_label_height = $size[1];
3947         } 
3948         // Fixed font
3949         else { // For Non-TTF fonts we can have only angles 0 or 90
3950             if ($this->x_label_angle == 90)
3951                 $this->x_tick_label_height = $this->max_t * $this->x_label_font['width'];
3952             else 
3953                 $this->x_tick_label_height = $this->x_label_font['height'];
3954         }
3955
3956         return TRUE;
3957     }
3958
3959
3960     /*!
3961      * \deprecated Calculates Maximum Y-Axis tick label width. Now inside CalcMargins()
3962      */
3963     function CalcYWidths() 
3964     {
3965         //the "." is for space. It isn't actually printed
3966         $ylab = number_format($this->max_y, $this->y_precision, '.', ', ') . $this->data_units_text . '.';
3967
3968         // TTF
3969         if ($this->use_ttf) {
3970             // Maximum Y tick label width
3971             $size = $this->TTFBBoxSize($this->y_label_font['size'], 0, $this->y_label_font['font'], $ylab);
3972             $this->y_tick_label_width = $size[0];
3973
3974         } 
3975         // Fixed font
3976         else {
3977             // Y axis title width
3978             $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
3979         }
3980
3981         return TRUE;
3982     }
3983
3984     /*!
3985      * \deprecated Superfluous.
3986      */
3987     function DrawLabels() 
3988     {
3989         $this->DrawTitle();
3990         $this->DrawXTitle();
3991         $this->DrawYTitle();
3992     }
3993
3994     /*! 
3995      * Set up the image resource 'img'
3996      * \deprecated The constructor should init 'img'
3997      */
3998     function InitImage() 
3999     {
4000         $this->img = ImageCreate($this->image_width, $this->image_height);
4001
4002         if (! $this->img)
4003             $this->PrintError('InitImage(): Could not create image resource');
4004         return TRUE;
4005     }
4006
4007     /*!
4008      * \deprecated
4009      */
4010     function SetNewPlotAreaPixels($x1, $y1, $x2, $y2) 
4011     {
4012         //Like in GD 0, 0 is upper left set via pixel Coordinates
4013         $this->plot_area = array($x1, $y1, $x2, $y2);
4014         $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
4015         $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
4016         $this->y_top_margin = $this->plot_area[1];
4017
4018         if (isset($this->plot_max_x))
4019             $this->CalcTranslation();
4020
4021         return TRUE;
4022     }
4023
4024     /*!
4025      * \deprecated Use _SetRGBColor()
4026      */
4027     function SetColor($which_color) 
4028     { 
4029         $this->SetRGBColor($which_color);
4030         return TRUE;
4031     }
4032
4033     /* 
4034      * \deprecated Use SetLineWidths().
4035      */
4036     function SetLineWidth($which_lw) 
4037     {
4038
4039         $this->SetLineWidths($which_lw);
4040
4041         if (!$this->error_bar_line_width) { 
4042             $this->SetErrorBarLineWidth($which_lw);
4043         }
4044         return TRUE;
4045     }
4046
4047     /*!
4048      * \deprecated 
4049      */
4050     function DrawDashedLine($x1, $y1, $x2, $y2 , $dash_length, $dash_space, $color)
4051     {
4052         if ($dash_length)
4053             $dashes = array_fill(0, $dash_length, $color);
4054         else
4055             $dashes = array();
4056         if ($dash_space)
4057             $spaces = array_fill(0, $dash_space, IMG_COLOR_TRANSPARENT);
4058         else
4059             $spaces = array();
4060
4061         $style = array_merge($dashes, $spaces);
4062         ImageSetStyle($this->img, $style);
4063         ImageLine($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
4064     }        
4065 }  // class PHPlot
4066
4067
4068
4069 //////////////////////// 
4070
4071
4072 /*!
4073  * Pads an array with another or with itself.
4074  *  \param arr array  Original array (reference)
4075  *  \param size int   Size of the resulting array.
4076  *  \param arr2 array If specified, array to use for padding. If unspecified, pad with $arr.
4077  */
4078 function array_pad_array(&$arr, $size, $arr2=NULL)
4079 {
4080     if (! is_array($arr2)) {
4081         $arr2 = $arr;                           // copy the original array
4082     }
4083     while (count($arr) < $size)
4084         $arr = array_merge($arr, $arr2);        // append until done
4085 }
4086 ?>