ILIAS  release_5-0 Revision 5.0.0-1144-gc4397b1f870
phplot.php
Go to the documentation of this file.
1<?php
2
3/* $Id$ */
4
5/*
6 * PHPLOT Version 5.0.5
7 * Copyright (C) 1998-2008 Afan Ottenheimer. Released under
8 * the GPL and PHP licenses as stated in the the README file which should
9 * have been included with this document.
10 *
11 * Co-author and maintainer (2003-2005)
12 * Miguel de Benito Delgado <nonick AT vodafone DOT es>
13 *
14 * Maintainer (2006-present)
15 * <lbayuk AT users DOT sourceforge DOT net>
16 *
17 * Visit http://sourceforge.net/projects/phplot/
18 * for PHPlot documentation, downloads, and discussions.
19 *
20 * Requires PHP 5.2.x or later. (PHP 4 is unsupported as of Jan 2008)
21 */
22
23class PHPlot {
24
25 /* I have removed internal variable declarations, some isset() checking was required,
26 * but now the variables left are those which can be tweaked by the user. This is intended to
27 * be the first step towards moving most of the Set...() methods into a subclass which will be
28 * used only when strictly necessary. Many users will be able to put default values here in the
29 * class and thus avoid memory overhead and reduce parsing times.
30 */
32
33 var $is_inline = FALSE; // FALSE = Sends headers, TRUE = sends just raw image data
34 var $browser_cache = FALSE; // FALSE = Sends headers for browser to not cache the image,
35 // (only if is_inline = FALSE also)
36
37 var $safe_margin = 5; // Extra margin used in several places. In pixels
38
39 var $x_axis_position = ''; // Where to draw both axis (world coordinates),
40 var $y_axis_position = ''; // leave blank for X axis at 0 and Y axis at left of plot.
41
42 var $xscale_type = 'linear'; // linear, log
43 var $yscale_type = 'linear';
44
45//Fonts
46 var $use_ttf = FALSE; // Use True Type Fonts?
47 var $ttf_path = '.'; // Default path to look in for TT Fonts.
48 var $default_ttfont = 'benjamingothic.ttf';
49 var $line_spacing = 4; // Pixels between lines.
50
51 // Font angles: 0 or 90 degrees for fixed fonts, any for TTF
52 var $x_label_angle = 0; // For labels on X axis (tick and data)
53 var $y_label_angle = 0; // For labels on Y axis (tick and data)
54
55//Formats
56 var $file_format = 'png';
57 var $output_file = ''; // For output to a file instead of stdout
58
59//Data
60 var $data_type = 'text-data'; // text-data, data-data-error, data-data, text-data-single
61 var $plot_type= 'linepoints'; // bars, lines, linepoints, area, points, pie, thinbarline, squared
62
63 var $label_scale_position = 0.5; // Shifts data labes in pie charts. 1 = top, 0 = bottom
64 var $group_frac_width = 0.7; // Bars use this fraction (0 to 1) of a group's space
65 var $bar_extra_space = 0.5; // Number of extra bar's worth of space in a group
66 var $bar_width_adjust = 1; // 1 = bars of normal width, must be > 0
67
68 var $y_precision = 1;
69 var $x_precision = 1;
70
71 var $data_units_text = ''; // Units text for 'data' labels (i.e: '¤', '$', etc.)
72
73// Titles
74 var $title_txt = '';
75
76 var $x_title_txt = '';
77 var $x_title_pos = 'plotdown'; // plotdown, plotup, both, none
78
79 var $y_title_txt = '';
80 var $y_title_pos = 'plotleft'; // plotleft, plotright, both, none
81
82
83//Labels
84 // There are two types of labels in PHPlot:
85 // Tick labels: they follow the grid, next to ticks in axis. (DONE)
86 // they are drawn at grid drawing time, by DrawXTicks() and DrawYTicks()
87 // Data labels: they follow the data points, and can be placed on the axis or the plot (x/y) (TODO)
88 // they are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc.
89 // Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_lines
90
91 // Tick Labels
92 var $x_tick_label_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none
93 var $y_tick_label_pos = 'plotleft'; // plotleft, plotright, both, yaxis, none
94
95 // Data Labels:
96 var $x_data_label_pos = 'plotdown'; // plotdown, plotup, both, plot, all, none
97 var $y_data_label_pos = 'plotleft'; // plotleft, plotright, both, plot, all, plotin, none
98
99 var $draw_x_data_label_lines = FALSE; // Draw a line from the data point to the axis?
100 var $draw_y_data_label_lines = FALSE; // TODO
101
102 // Label types: (for tick, data and plot labels)
103 var $x_label_type = ''; // data, time. Leave blank for no formatting.
104 var $y_label_type = ''; // data, time. Leave blank for no formatting.
105 var $x_time_format = '%H:%M:%S'; // See http://www.php.net/manual/html/function.strftime.html
106 var $y_time_format = '%H:%M:%S'; // SetYTimeFormat() too...
107
108 // Skipping labels
109 // var $x_label_inc = 1; // Draw a label every this many (1 = all) (TODO)
110 // var $y_label_inc = 1;
111 // var $_x_label_cnt = 0; // internal count FIXME: work in progress
112
113// Legend
114 var $legend = ''; // An array with legend titles
115 // These variables are unset to take default values:
116 // var $legend_x_pos; // User-specified upper left coordinates of legend box
117 // var $legend_y_pos;
118 // var $legend_xy_world; // If set, legend_x/y_pos are world coords, else pixel coords
119 // var $legend_text_align; // left or right, Unset means right
120 // var $legend_colorbox_align; // left, right, or none; Unset means same as text_align
121
122//Ticks
123 var $x_tick_length = 5; // tick length in pixels for upper/lower axis
124 var $y_tick_length = 5; // tick length in pixels for left/right axis
125
126 var $x_tick_cross = 3; // ticks cross x axis this many pixels
127 var $y_tick_cross = 3; // ticks cross y axis this many pixels
128
129 var $x_tick_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none
130 var $y_tick_pos = 'plotleft'; // plotright, plotleft, both, yaxis, none
131
132 var $num_x_ticks = '';
133 var $num_y_ticks = '';
134
135 var $x_tick_inc = ''; // Set num_x_ticks or x_tick_inc, not both.
136 var $y_tick_inc = ''; // Set num_y_ticks or y_tick_inc, not both.
137
138 var $skip_top_tick = FALSE;
139 var $skip_bottom_tick = FALSE;
140 var $skip_left_tick = FALSE;
141 var $skip_right_tick = FALSE;
142
143//Grid Formatting
144 var $draw_x_grid = FALSE;
145 var $draw_y_grid = TRUE;
146
147 var $dashed_grid = TRUE;
148 var $grid_at_foreground = FALSE; // Chooses whether to draw the grid below or above the graph
149
150//Colors and styles (all colors can be array (R,G,B) or named color)
151 var $color_array = 'small'; // 'small', 'large' or array (define your own colors)
152 // See rgb.inc.php and SetRGBArray()
153 var $i_border = array(194, 194, 194);
154 var $plot_bg_color = 'white';
155 var $bg_color = 'white';
156 var $label_color = 'black';
157 var $text_color = 'black';
158 var $grid_color = 'black';
159 var $light_grid_color = 'gray';
160 var $tick_color = 'black';
161 var $title_color = 'black';
162 var $data_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
163 var $error_bar_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
164 var $data_border_colors = array('black');
165
166 var $line_widths = 1; // single value or array
167 var $line_styles = array('solid', 'solid', 'dashed'); // single value or array
168 var $dashed_style = '2-4'; // colored dots-transparent dots
169
170 var $point_sizes = array(5,5,3); // single value or array
171 var $point_shapes = array('diamond'); // rect, circle, diamond, triangle, dot, line, halfline, cross
172
173 var $error_bar_size = 5; // right and left size of tee
174 var $error_bar_shape = 'tee'; // 'tee' or 'line'
175 var $error_bar_line_width = 1; // single value (or array TODO)
176
177 var $plot_border_type = 'sides'; // left, sides, none, full
178 var $image_border_type = 'none'; // 'raised', 'plain', 'none'
179
180 var $shading = 5; // 0 for no shading, > 0 is size of shadows in pixels
181
183 var $draw_broken_lines = FALSE; // Tells not to draw lines for missing Y data.
184
185//Miscellaneous
186 var $callbacks = array( // Valid callback reasons (see SetCallBack)
187 'draw_setup' => NULL,
188 'draw_image_background' => NULL,
189 'draw_plotarea_background' => NULL,
190 'draw_titles' => NULL,
191 'draw_axes' => NULL,
192 'draw_graph' => NULL,
193 'draw_border' => NULL,
194 'draw_legend' => NULL,
195 'debug_textbox' => NULL, // For testing/debugging text box alignment
196 'debug_scale' => NULL, // For testing/debugging scale setup
197 );
198
199
201//BEGIN CODE
203
212 function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
213 {
214 $this->SetRGBArray($this->color_array);
215
216 $this->background_done = FALSE; // TRUE after background image is drawn once
217 $this->plot_margins_set = FALSE; // TRUE with user-set plot area or plot margins.
218
219 if ($which_output_file)
220 $this->SetOutputFile($which_output_file);
221
222 if ($which_input_file)
223 $this->SetInputFile($which_input_file);
224 else {
225 $this->image_width = $which_width;
226 $this->image_height = $which_height;
227
228 $this->img = ImageCreate($this->image_width, $this->image_height);
229 if (! $this->img)
230 return $this->PrintError('PHPlot(): Could not create image resource.');
231
232 }
233
234 $this->SetDefaultStyles();
235 $this->SetDefaultFonts();
236
237 $this->SetTitle('');
238 $this->SetXTitle('');
239 $this->SetYTitle('');
240
241 $this->print_image = TRUE; // Use for multiple plots per image (TODO: automatic)
242 }
243
249 function GetImage($image_filename, &$width, &$height)
250 {
251 $error = '';
252 $size = getimagesize($image_filename);
253 if (!$size) {
254 $error = "Unable to query image file $image_filename";
255 } else {
256 $image_type = $size[2];
257 switch($image_type) {
258 case IMAGETYPE_GIF:
259 $img = @ ImageCreateFromGIF ($image_filename);
260 break;
261 case IMAGETYPE_PNG:
262 $img = @ ImageCreateFromPNG ($image_filename);
263 break;
264 case IMAGETYPE_JPEG:
265 $img = @ ImageCreateFromJPEG ($image_filename);
266 break;
267 default:
268 $error = "Unknown image type ($image_type) for image file $image_filename";
269 break;
270 }
271 }
272 if (empty($error) && !$img) {
273 # getimagesize is OK, but GD won't read it. Maybe unsupported format.
274 $error = "Failed to read image file $image_filename";
275 }
276 if (!empty($error)) {
277 return $this->PrintError("GetImage(): $error");
278 }
279 $width = $size[0];
280 $height = $size[1];
281 return $img;
282 }
283
289 function SetInputFile($which_input_file)
290 {
291 $im = $this->GetImage($which_input_file, $this->image_width, $this->image_height);
292 if (!$im)
293 return FALSE; // GetImage already produced an error message.
294
295 // Deallocate any resources previously allocated
296 if (isset($this->img))
297 imagedestroy($this->img);
298
299 $this->img = $im;
300
301 // Do not overwrite the input file with the background color.
302 $this->background_done = TRUE;
303
304 return TRUE;
305 }
306
310
317 function SetIndexColor($which_color)
318 {
319 list ($r, $g, $b) = $this->SetRGBColor($which_color); //Translate to RGB
320 if (!isset($r)) return NULL;
321 return ImageColorResolve($this->img, $r, $g, $b);
322 }
323
324
329 function SetIndexDarkColor($which_color)
330 {
331 list ($r, $g, $b) = $this->SetRGBColor($which_color);
332 if (!isset($r)) return NULL;
333 $r = max(0, $r - 0x30);
334 $g = max(0, $g - 0x30);
335 $b = max(0, $b - 0x30);
336 return ImageColorResolve($this->img, $r, $g, $b);
337 }
338
348 {
349 /* Some of the Set*() functions use default values when they get no parameters. */
350
351 if (! isset($this->session_set)) {
352 // If sessions are enabled, this variable will be preserved, so upon future executions, we
353 // will have it set, as well as color names (though not color indices, that's why we
354 // need to rebuild them)
355 $this->session_set = TRUE;
356
357 // These only need to be set once
358 $this->SetLineWidths();
359 $this->SetLineStyles();
360 $this->SetDefaultDashedStyle($this->dashed_style);
361 $this->SetPointSizes($this->point_sizes);
362 }
363
364 $this->SetImageBorderColor($this->i_border);
365 $this->SetPlotBgColor($this->plot_bg_color);
366 $this->SetBackgroundColor($this->bg_color);
367 $this->SetLabelColor($this->label_color);
368 $this->SetTextColor($this->text_color);
369 $this->SetGridColor($this->grid_color);
370 $this->SetLightGridColor($this->light_grid_color);
371 $this->SetTickColor($this->tick_color);
372 $this->SetTitleColor($this->title_color);
373 $this->SetDataColors();
374 $this->SetErrorBarColors();
375 $this->SetDataBorderColors();
376 return TRUE;
377 }
378
379
380 /*
381 *
382 */
383 function SetBackgroundColor($which_color)
384 {
385 $this->bg_color= $which_color;
386 $this->ndx_bg_color= $this->SetIndexColor($this->bg_color);
387 return isset($this->ndx_bg_color);
388 }
389
390 /*
391 *
392 */
393 function SetPlotBgColor($which_color)
394 {
395 $this->plot_bg_color= $which_color;
396 $this->ndx_plot_bg_color= $this->SetIndexColor($this->plot_bg_color);
397 return isset($this->ndx_plot_bg_color);
398 }
399
400 /*
401 *
402 */
403 function SetTitleColor($which_color)
404 {
405 $this->title_color= $which_color;
406 $this->ndx_title_color= $this->SetIndexColor($this->title_color);
407 return isset($this->ndx_title_color);
408 }
409
410 /*
411 *
412 */
413 function SetTickColor ($which_color)
414 {
415 $this->tick_color= $which_color;
416 $this->ndx_tick_color= $this->SetIndexColor($this->tick_color);
417 return isset($this->ndx_tick_color);
418 }
419
420
421 /*
422 * Do not use. Use SetTitleColor instead.
423 */
424 function SetLabelColor ($which_color)
425 {
426 $this->label_color = $which_color;
427 $this->ndx_title_color= $this->SetIndexColor($this->label_color);
428 return isset($this->ndx_title_color);
429 }
430
431
432 /*
433 *
434 */
435 function SetTextColor ($which_color)
436 {
437 $this->text_color= $which_color;
438 $this->ndx_text_color= $this->SetIndexColor($this->text_color);
439 return isset($this->ndx_text_color);
440 }
441
442
443 /*
444 *
445 */
446 function SetLightGridColor ($which_color)
447 {
448 $this->light_grid_color= $which_color;
449 $this->ndx_light_grid_color= $this->SetIndexColor($this->light_grid_color);
450 return isset($this->ndx_light_grid_color);
451 }
452
453
454 /*
455 *
456 */
457 function SetGridColor ($which_color)
458 {
459 $this->grid_color = $which_color;
460 $this->ndx_grid_color= $this->SetIndexColor($this->grid_color);
461 return isset($this->ndx_grid_color);
462 }
463
464
465 /*
466 *
467 */
468 function SetImageBorderColor($which_color)
469 {
470 $this->i_border = $which_color;
471 $this->ndx_i_border = $this->SetIndexColor($this->i_border);
472 $this->ndx_i_border_dark = $this->SetIndexDarkColor($this->i_border);
473 return isset($this->ndx_i_border);
474 }
475
476
477 /*
478 *
479 */
480 function SetTransparentColor($which_color)
481 {
482 $ndx = $this->SetIndexColor($which_color);
483 if (!isset($ndx))
484 return FALSE;
485 ImageColorTransparent($this->img, $ndx);
486 return TRUE;
487 }
488
489
497 function SetRGBArray ($which_color_array)
498 {
499 if ( is_array($which_color_array) ) { // User defined array
500 $this->rgb_array = $which_color_array;
501 return TRUE;
502 } elseif ($which_color_array == 'small') { // Small predefined color array
503 $this->rgb_array = array(
504 'white' => array(255, 255, 255),
505 'snow' => array(255, 250, 250),
506 'PeachPuff' => array(255, 218, 185),
507 'ivory' => array(255, 255, 240),
508 'lavender' => array(230, 230, 250),
509 'black' => array( 0, 0, 0),
510 'DimGrey' => array(105, 105, 105),
511 'gray' => array(190, 190, 190),
512 'grey' => array(190, 190, 190),
513 'navy' => array( 0, 0, 128),
514 'SlateBlue' => array(106, 90, 205),
515 'blue' => array( 0, 0, 255),
516 'SkyBlue' => array(135, 206, 235),
517 'cyan' => array( 0, 255, 255),
518 'DarkGreen' => array( 0, 100, 0),
519 'green' => array( 0, 255, 0),
520 'YellowGreen' => array(154, 205, 50),
521 'yellow' => array(255, 255, 0),
522 'orange' => array(255, 165, 0),
523 'gold' => array(255, 215, 0),
524 'peru' => array(205, 133, 63),
525 'beige' => array(245, 245, 220),
526 'wheat' => array(245, 222, 179),
527 'tan' => array(210, 180, 140),
528 'brown' => array(165, 42, 42),
529 'salmon' => array(250, 128, 114),
530 'red' => array(255, 0, 0),
531 'pink' => array(255, 192, 203),
532 'maroon' => array(176, 48, 96),
533 'magenta' => array(255, 0, 255),
534 'violet' => array(238, 130, 238),
535 'plum' => array(221, 160, 221),
536 'orchid' => array(218, 112, 214),
537 'purple' => array(160, 32, 240),
538 'azure1' => array(240, 255, 255),
539 'aquamarine1' => array(127, 255, 212)
540 );
541 return TRUE;
542 } elseif ($which_color_array === 'large') { // Large color array
543 include("./rgb.inc.php");
544 $this->rgb_array = $RGBArray;
545 } else { // Default to black and white only.
546 $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0));
547 }
548
549 return TRUE;
550 }
551
557 function SetRGBColor($color_asked)
558 {
559 if (empty($color_asked)) {
560 $ret_val = array(0, 0, 0);
561 } elseif (count($color_asked) == 3 ) { // already array of 3 rgb
562 $ret_val = $color_asked;
563 } elseif ($color_asked[0] == '#') { // Hex RGB notation #RRGGBB
564 $ret_val = array(hexdec(substr($color_asked, 1, 2)),
565 hexdec(substr($color_asked, 3, 2)),
566 hexdec(substr($color_asked, 5, 2)));
567
568 } elseif (isset($this->rgb_array[$color_asked])) { // Color by name
569 $ret_val = $this->rgb_array[$color_asked];
570 } else {
571 return $this->PrintError("SetRGBColor(): Color '$color_asked' is not valid.");
572 }
573 return $ret_val;
574 }
575
576
580 function SetDataColors($which_data = NULL, $which_border = NULL)
581 {
582 if (is_null($which_data) && is_array($this->data_colors)) {
583 // use already set data_colors
584 } else if (! is_array($which_data)) {
585 $this->data_colors = ($which_data) ? array($which_data) : array('blue', 'red', 'green', 'orange');
586 } else {
587 $this->data_colors = $which_data;
588 }
589
590 $i = 0;
591 foreach ($this->data_colors as $col) {
592 $ndx = $this->SetIndexColor($col);
593 if (!isset($ndx))
594 return FALSE;
595 $this->ndx_data_colors[$i] = $ndx;
596 $this->ndx_data_dark_colors[$i] = $this->SetIndexDarkColor($col);
597 $i++;
598 }
599
600 // For past compatibility:
601 return $this->SetDataBorderColors($which_border);
602 } // function SetDataColors()
603
604
608 function SetDataBorderColors($which_br = NULL)
609 {
610 if (is_null($which_br) && is_array($this->data_border_colors)) {
611 // use already set data_border_colors
612 } else if (! is_array($which_br)) {
613 // Create new array with specified color
614 $this->data_border_colors = ($which_br) ? array($which_br) : array('black');
615 } else {
616 $this->data_border_colors = $which_br;
617 }
618
619 $i = 0;
620 foreach($this->data_border_colors as $col) {
621 $ndx = $this->SetIndexColor($col);
622 if (!isset($ndx))
623 return FALSE;
624 $this->ndx_data_border_colors[$i] = $ndx;
625 $i++;
626 }
627 return TRUE;
628 } // function SetDataBorderColors()
629
630
634 function SetErrorBarColors($which_err = NULL)
635 {
636 if (is_null($which_err) && is_array($this->error_bar_colors)) {
637 // use already set error_bar_colors
638 } else if (! is_array($which_err)) {
639 $this->error_bar_colors = ($which_err) ? array($which_err) : array('black');
640 } else {
641 $this->error_bar_colors = $which_err;
642 }
643
644 $i = 0;
645 foreach($this->error_bar_colors as $col) {
646 $ndx = $this->SetIndexColor($col);
647 if (!isset($ndx))
648 return FALSE;
649 $this->ndx_error_bar_colors[$i] = $ndx;
650 $i++;
651 }
652 return TRUE;
653 } // function SetErrorBarColors()
654
655
662 function SetDefaultDashedStyle($which_style)
663 {
664 // String: "numcol-numtrans-numcol-numtrans..."
665 $asked = explode('-', $which_style);
666
667 if (count($asked) < 2) {
668 return $this->PrintError("SetDefaultDashedStyle(): Wrong parameter '$which_style'.");
669 }
670
671 // Build the string to be eval()uated later by SetDashedStyle()
672 $this->default_dashed_style = 'array( ';
673
674 $t = 0;
675 foreach($asked as $s) {
676 if ($t % 2 == 0) {
677 $this->default_dashed_style .= str_repeat('$which_ndxcol,', $s);
678 } else {
679 $this->default_dashed_style .= str_repeat('IMG_COLOR_TRANSPARENT,', $s);
680 }
681 $t++;
682 }
683 // Remove trailing comma and add closing parenthesis
684 $this->default_dashed_style = substr($this->default_dashed_style, 0, -1);
685 $this->default_dashed_style .= ')';
686
687 return TRUE;
688 }
689
690
695 function SetDashedStyle($which_ndxcol)
696 {
697 // See SetDefaultDashedStyle() to understand this.
698 eval ("\$style = $this->default_dashed_style;");
699 return imagesetstyle($this->img, $style);
700 }
701
702
706 function SetLineWidths($which_lw=NULL)
707 {
708 if (is_null($which_lw)) {
709 // Do nothing, use default value.
710 } else if (is_array($which_lw)) {
711 // Did we get an array with line widths?
712 $this->line_widths = $which_lw;
713 } else {
714 $this->line_widths = array($which_lw);
715 }
716 return TRUE;
717 }
718
722 function SetLineStyles($which_ls=NULL)
723 {
724 if (is_null($which_ls)) {
725 // Do nothing, use default value.
726 } else if ( is_array($which_ls)) {
727 // Did we get an array with line styles?
728 $this->line_styles = $which_ls;
729 } else {
730 $this->line_styles = ($which_ls) ? array($which_ls) : array('solid');
731 }
732 return TRUE;
733 }
734
735
739
740
744 function SetLineSpacing($which_spc)
745 {
746 $this->line_spacing = $which_spc;
747 return TRUE;
748 }
749
750
756 function SetUseTTF($which_ttf)
757 {
758 $this->use_ttf = $which_ttf;
759 return $this->SetDefaultFonts();
760 }
761
765 function SetTTFPath($which_path)
766 {
767 // Maybe someone needs really dynamic config. He'll need this:
768 // clearstatcache();
769
770 if (is_dir($which_path) && is_readable($which_path)) {
771 $this->ttf_path = $which_path;
772 return TRUE;
773 }
774 return $this->PrintError("SetTTFPath(): $which_path is not a valid path.");
775 }
776
783 function SetDefaultTTFont($which_font)
784 {
785 $this->default_ttfont = $which_font;
786 return $this->SetUseTTF(TRUE);
787 }
788
793 {
794 // TTF:
795 if ($this->use_ttf) {
796 return $this->SetFont('generic', '', 8)
797 && $this->SetFont('title', '', 14)
798 && $this->SetFont('legend', '', 8)
799 && $this->SetFont('x_label', '', 6)
800 && $this->SetFont('y_label', '', 6)
801 && $this->SetFont('x_title', '', 10)
802 && $this->SetFont('y_title', '', 10);
803 }
804 // Fixed GD Fonts:
805 return $this->SetFont('generic', 2)
806 && $this->SetFont('title', 5)
807 && $this->SetFont('legend', 2)
808 && $this->SetFont('x_label', 1)
809 && $this->SetFont('y_label', 1)
810 && $this->SetFont('x_title', 3)
811 && $this->SetFont('y_title', 3);
812 }
813
825 function SetFont($which_elem, $which_font, $which_size = 12)
826 {
827 // TTF:
828 if ($this->use_ttf) {
829 // Empty font name means use the default font.
830 if (empty($which_font))
831 $which_font = $this->default_ttfont;
832 $path = $which_font;
833
834 // First try the font name directly, if not then try with path.
835 if (!is_file($path) || !is_readable($path)) {
836 $path = $this->ttf_path . '/' . $which_font;
837 if (!is_file($path) || !is_readable($path)) {
838 return $this->PrintError("SetFont(): Can't find TrueType font $which_font");
839 }
840 }
841
842 switch ($which_elem) {
843 case 'generic':
844 $this->generic_font['font'] = $path;
845 $this->generic_font['size'] = $which_size;
846 break;
847 case 'title':
848 $this->title_font['font'] = $path;
849 $this->title_font['size'] = $which_size;
850 break;
851 case 'legend':
852 $this->legend_font['font'] = $path;
853 $this->legend_font['size'] = $which_size;
854 break;
855 case 'x_label':
856 $this->x_label_font['font'] = $path;
857 $this->x_label_font['size'] = $which_size;
858 break;
859 case 'y_label':
860 $this->y_label_font['font'] = $path;
861 $this->y_label_font['size'] = $which_size;
862 break;
863 case 'x_title':
864 $this->x_title_font['font'] = $path;
865 $this->x_title_font['size'] = $which_size;
866 break;
867 case 'y_title':
868 $this->y_title_font['font'] = $path;
869 $this->y_title_font['size'] = $which_size;
870 break;
871 default:
872 return $this->PrintError("SetFont(): Unknown element '$which_elem' specified.");
873 }
874 return TRUE;
875
876 }
877
878 // Fixed fonts:
879 if ($which_font > 5 || $which_font < 0) {
880 return $this->PrintError('SetFont(): Non-TTF font size must be 1, 2, 3, 4 or 5');
881 }
882
883 switch ($which_elem) {
884 case 'generic':
885 $this->generic_font['font'] = $which_font;
886 $this->generic_font['height'] = ImageFontHeight($which_font);
887 $this->generic_font['width'] = ImageFontWidth($which_font);
888 break;
889 case 'title':
890 $this->title_font['font'] = $which_font;
891 $this->title_font['height'] = ImageFontHeight($which_font);
892 $this->title_font['width'] = ImageFontWidth($which_font);
893 break;
894 case 'legend':
895 $this->legend_font['font'] = $which_font;
896 $this->legend_font['height'] = ImageFontHeight($which_font);
897 $this->legend_font['width'] = ImageFontWidth($which_font);
898 break;
899 case 'x_label':
900 $this->x_label_font['font'] = $which_font;
901 $this->x_label_font['height'] = ImageFontHeight($which_font);
902 $this->x_label_font['width'] = ImageFontWidth($which_font);
903 break;
904 case 'y_label':
905 $this->y_label_font['font'] = $which_font;
906 $this->y_label_font['height'] = ImageFontHeight($which_font);
907 $this->y_label_font['width'] = ImageFontWidth($which_font);
908 break;
909 case 'x_title':
910 $this->x_title_font['font'] = $which_font;
911 $this->x_title_font['height'] = ImageFontHeight($which_font);
912 $this->x_title_font['width'] = ImageFontWidth($which_font);
913 break;
914 case 'y_title':
915 $this->y_title_font['font'] = $which_font;
916 $this->y_title_font['height'] = ImageFontHeight($which_font);
917 $this->y_title_font['width'] = ImageFontWidth($which_font);
918 break;
919 default:
920 return $this->PrintError("SetFont(): Unknown element '$which_elem' specified.");
921 }
922 return TRUE;
923 }
924
988 /*
989 * ProcessTextGD() - Draw or size GD fixed-font text.
990 * This is intended for use only by ProcessText().
991 * $draw_it : True to draw the text, False to just return the orthogonal width and height.
992 * $font_number : GD font number, 1-5.
993 * $font_width : Fixed character cell width for the font
994 * $font_height : Fixed character cell height for the font
995 * $angle : Text angle in degrees. GD only supports 0 and 90. We treat >= 45 as 90, else 0.
996 * $x, $y : Reference point for the text (ignored if !$draw_it)
997 * $color : GD color index to use for drawing the text (ignored if !$draw_it)
998 * $text : The text to draw or size. Put a newline between lines.
999 * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it)
1000 * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it)
1001 * Returns: True, if drawing text, or an array of ($width, $height) if not.
1002 */
1003 function ProcessTextGD($draw_it, $font_number, $font_width, $font_height, $angle, $x, $y, $color,
1004 $text, $h_factor, $v_factor)
1005 {
1006 # Break up the text into lines, trim whitespace, find longest line.
1007 # Save the lines and length for drawing below.
1008 $longest = 0;
1009 foreach (explode("\n", $text) as $each_line) {
1010 $lines[] = $line = trim($each_line);
1011 $line_lens[] = $line_len = strlen($line);
1012 if ($line_len > $longest) $longest = $line_len;
1013 }
1014 $n_lines = count($lines);
1015 $spacing = $this->line_spacing * ($n_lines - 1); // Total inter-line spacing
1016
1017 # Width, height are based on font size and longest line, line count respectively.
1018 # These are relative to the text angle.
1019 $total_width = $longest * $font_width;
1020 $total_height = $n_lines * $font_height + $spacing;
1021
1022 if (!$draw_it) {
1023 if ($angle < 45) return array($total_width, $total_height);
1024 return array($total_height, $total_width);
1025 }
1026
1027 $interline_step = $font_height + $this->line_spacing; // Line-to-line step
1028
1029 if ($angle >= 45) {
1030 // Vertical text (90 degrees):
1031 // (Remember the alignment convention with vertical text)
1032 // For 90 degree text, alignment factors change like this:
1033 $temp = $v_factor;
1034 $v_factor = $h_factor;
1035 $h_factor = 1 - $temp;
1036
1037 $draw_func = 'ImageStringUp';
1038
1039 // Rotation matrix "R" for 90 degrees (with Y pointing down):
1040 $r00 = 0; $r01 = 1;
1041 $r10 = -1; $r11 = 0;
1042
1043 } else {
1044 // Horizontal text (0 degrees):
1045 $draw_func = 'ImageString';
1046
1047 // Rotation matrix "R" for 0 degrees:
1048 $r00 = 1; $r01 = 0;
1049 $r10 = 0; $r11 = 1;
1050 }
1051
1052 // Adjust for vertical alignment (horizontal text) or horizontal aligment (vertical text):
1053 $factor = (int)($total_height * $v_factor);
1054 $xpos = $x - $r01 * $factor;
1055 $ypos = $y - $r11 * $factor;
1056
1057 # Debug callback provides the bounding box:
1058 if ($this->GetCallback('debug_textbox')) {
1059 if ($angle >= 45) {
1060 $bbox_width = $total_height;
1061 $bbox_height = $total_width;
1062 $px = $xpos;
1063 $py = $ypos - (1 - $h_factor) * $total_width;
1064 } else {
1065 $bbox_width = $total_width;
1066 $bbox_height = $total_height;
1067 $px = $xpos - $h_factor * $total_width;
1068 $py = $ypos;
1069 }
1070 $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height);
1071 }
1072
1073 for ($i = 0; $i < $n_lines; $i++) {
1074
1075 $line = $lines[$i];
1076 $line_len = $line_lens[$i];
1077
1078 // Adjust for alignment of this line within the text block:
1079 $factor = (int)($line_len * $font_width * $h_factor);
1080 $x = $xpos - $r00 * $factor;
1081 $y = $ypos - $r10 * $factor;
1082
1083 // Call ImageString or ImageStringUp:
1084 $draw_func($this->img, $font_number, $x, $y, $line, $color);
1085
1086 // Step to the next line of text. This is a rotation of (x=0, y=interline_spacing)
1087 $xpos += $r01 * $interline_step;
1088 $ypos += $r11 * $interline_step;
1089 }
1090 return TRUE;
1091 }
1092
1093
1094 /*
1095 * ProcessTextTTF() - Draw or size TTF text.
1096 * This is intended for use only by ProcessText().
1097 * $draw_it : True to draw the text, False to just return the orthogonal width and height.
1098 * $font_file : Path or filename to a TTF font file.
1099 * $font_size : Font size in "points".
1100 * $angle : Text angle in degrees.
1101 * $x, $y : Reference point for the text (ignored if !$draw_it)
1102 * $color : GD color index to use for drawing the text (ignored if !$draw_it)
1103 * $text : The text to draw or size. Put a newline between lines.
1104 * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it)
1105 * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it)
1106 * Returns: True, if drawing text, or an array of ($width, $height) if not.
1107 */
1108 function ProcessTextTTF($draw_it, $font_file, $font_size, $angle, $x, $y, $color,
1109 $text, $h_factor, $v_factor)
1110 {
1111 # Break up the text into lines, trim whitespace.
1112 # Calculate the total width and height of the text box at 0 degrees.
1113 # This has to be done line-by-line so the interline-spacing is right.
1114 # Save the trimmed line, and its 0-degree height and width for later
1115 # when the text is drawn.
1116 # Note: Total height = sum of each line height plus inter-line spacing
1117 # (which is added after the loop). Total width = width of widest line.
1118 $total_height = 0;
1119 $total_width = 0;
1120 foreach (explode("\n", $text) as $each_line) {
1121 $lines[] = $line = trim($each_line);
1122 $bbox = ImageTTFBBox($font_size, 0, $font_file, $line);
1123 $height = $bbox[1] - $bbox[5];
1124 $width = $bbox[2] - $bbox[0];
1125 $total_height += $height;
1126 if ($width > $total_width) $total_width = $width;
1127 $line_widths[] = $width;
1128 $line_heights[] = $height;
1129 }
1130 $n_lines = count($lines);
1131 $total_height += ($n_lines - 1) * $this->line_spacing;
1132
1133 # Calculate the rotation matrix for the text's angle. Remember that GD points Y down,
1134 # so the sin() terms change sign.
1135 $theta = deg2rad($angle);
1136 $cos_t = cos($theta);
1137 $sin_t = sin($theta);
1138 $r00 = $cos_t; $r01 = $sin_t;
1139 $r10 = -$sin_t; $r11 = $cos_t;
1140
1141 # Make a bounding box of the right size, with upper left corner at (0,0).
1142 # By convention, the point order is: LL, LR, UR, UL.
1143 # Note this is still working with the text at 0 degrees.
1144 $b[0] = 0; $b[1] = $total_height;
1145 $b[2] = $total_width; $b[3] = $b[1];
1146 $b[4] = $b[2]; $b[5] = 0;
1147 $b[6] = $b[0]; $b[7] = $b[5];
1148
1149 # Rotate the bounding box, then offset to the reference point:
1150 for ($i = 0; $i < 8; $i += 2) {
1151 $x_b = $b[$i];
1152 $y_b = $b[$i+1];
1153 $c[$i] = $x + $r00 * $x_b + $r01 * $y_b;
1154 $c[$i+1] = $y + $r10 * $x_b + $r11 * $y_b;
1155 }
1156
1157 # Get an orthogonal (aligned with X and Y axes) bounding box around it, by
1158 # finding the min and max X and Y:
1159 $bbox_ref_x = $bbox_max_x = $c[0];
1160 $bbox_ref_y = $bbox_max_y = $c[1];
1161 for ($i = 2; $i < 8; $i += 2) {
1162 $x_b = $c[$i];
1163 if ($x_b < $bbox_ref_x) $bbox_ref_x = $x_b; elseif ($bbox_max_x < $x_b) $bbox_max_x = $x_b;
1164 $y_b = $c[$i+1];
1165 if ($y_b < $bbox_ref_y) $bbox_ref_y = $y_b; elseif ($bbox_max_y < $y_b) $bbox_max_y = $y_b;
1166 }
1167 $bbox_width = $bbox_max_x - $bbox_ref_x;
1168 $bbox_height = $bbox_max_y - $bbox_ref_y;
1169
1170 if (!$draw_it) {
1171 # Return the bounding box, rounded up (so it always contains the text):
1172 return array((int)ceil($bbox_width), (int)ceil($bbox_height));
1173 }
1174
1175 # Calculate the offsets from the supplied reference point to the
1176 # required upper-left corner of the text.
1177 # Start at the reference point at the upper left corner of the bounding
1178 # box (bbox_ref_x, bbox_ref_y) then adjust it for the 9 point alignment.
1179 # h,v_factor are 0,0 for top,left, .5,.5 for center,center, 1,1 for bottom,right.
1180 # $off_x = $bbox_ref_x + $bbox_width * $h_factor - $x;
1181 # $off_y = $bbox_ref_y + $bbox_height * $v_factor - $y;
1182 # Then use that offset to calculate back to the supplied reference point x, y
1183 # to get the text base point.
1184 # $qx = $x - $off_x;
1185 # $qy = $y - $off_y;
1186 # Reduces to:
1187 $qx = 2 * $x - $bbox_ref_x - $bbox_width * $h_factor;
1188 $qy = 2 * $y - $bbox_ref_y - $bbox_height * $v_factor;
1189
1190 # Check for debug callback. Don't calculate bounding box unless it is wanted.
1191 if ($this->GetCallback('debug_textbox')) {
1192 # Calculate the orthogonal bounding box coordinates for debug testing.
1193
1194 # qx, qy is upper left corner relative to the text.
1195 # Calculate px,py: upper left corner (absolute) of the bounding box.
1196 # There are 4 equation sets for this, depending on the quadrant:
1197 if ($sin_t > 0) {
1198 if ($cos_t > 0) {
1199 # Quadrant: 0d - 90d:
1200 $px = $qx; $py = $qy - $total_width * $sin_t;
1201 } else {
1202 # Quadrant: 90d - 180d:
1203 $px = $qx + $total_width * $cos_t; $py = $qy - $bbox_height;
1204 }
1205 } else {
1206 if ($cos_t < 0) {
1207 # Quadrant: 180d - 270d:
1208 $px = $qx - $bbox_width; $py = $qy + $total_height * $cos_t;
1209 } else {
1210 # Quadrant: 270d - 360d:
1211 $px = $qx + $total_height * $sin_t; $py = $qy;
1212 }
1213 }
1214 $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height);
1215 }
1216
1217 # Since alignment is applied after rotation, which parameter is used
1218 # to control alignment of each line within the text box varies with
1219 # the angle.
1220 # Angle (degrees): Line alignment controlled by:
1221 # -45 < angle <= 45 h_align
1222 # 45 < angle <= 135 reversed v_align
1223 # 135 < angle <= 225 reversed h_align
1224 # 225 < angle <= 315 v_align
1225 if ($cos_t >= $sin_t) {
1226 if ($cos_t >= -$sin_t) $line_align_factor = $h_factor;
1227 else $line_align_factor = $v_factor;
1228 } else {
1229 if ($cos_t >= -$sin_t) $line_align_factor = 1-$v_factor;
1230 else $line_align_factor = 1-$h_factor;
1231 }
1232
1233 # Now we have the start point, spacing and in-line alignment factor.
1234 # We are finally ready to start drawing the text, line by line.
1235 for ($i = 0; $i < $n_lines; $i++) {
1236 $line = $lines[$i];
1237 # These are height and width at 0 degrees, calculated above:
1238 $height = $line_heights[$i];
1239 $width = $line_widths[$i];
1240
1241 # For drawing TTF text, the lower left corner is the base point.
1242 # The adjustment is inside the loop, since lines can have different
1243 # heights. The following also adjusts for horizontal (relative to
1244 # the text) alignment of the current line within the box.
1245 # What is happening is rotation of this vector by the text angle:
1246 # (x = (total_width - line_width) * factor, y = line_height)
1247
1248 $width_factor = ($total_width - $width) * $line_align_factor;
1249 $rx = $qx + $r00 * $width_factor + $r01 * $height;
1250 $ry = $qy + $r10 * $width_factor + $r11 * $height;
1251
1252 # Finally, draw the text:
1253 ImageTTFText($this->img, $font_size, $angle, $rx, $ry, $color, $font_file, $line);
1254
1255 # Step to position of next line.
1256 # This is a rotation of (x=0,y=text_height+line_height) by $angle:
1257 $interline_step = $this->line_spacing + $height;
1258 $qx += $r01 * $interline_step;
1259 $qy += $r11 * $interline_step;
1260 }
1261 return True;
1262 }
1263
1264 /*
1265 * ProcessText() - Wrapper for ProcessTextTTF() and ProcessTextGD(). See notes above.
1266 * This is intended for use from within PHPlot only, and only by DrawText() and SizeText().
1267 * $draw_it : True to draw the text, False to just return the orthogonal width and height.
1268 * $font : PHPlot font array. (Contents depend on use_ttf flag).
1269 * $angle : Text angle in degrees
1270 * $x, $y : Reference point for the text (ignored if !$draw_it)
1271 * $color : GD color index to use for drawing the text (ignored if !$draw_it)
1272 * $text : The text to draw or size. Put a newline between lines.
1273 * $halign : Horizontal alignment: left, center, or right (ignored if !$draw_it)
1274 * $valign : Vertical alignment: top, center, or bottom (ignored if !$draw_it)
1275 * Note: Alignment is relative to the image, not the text.
1276 * Returns: True, if drawing text, or an array of ($width, $height) if not.
1277 */
1278 function ProcessText($draw_it, $font, $angle, $x, $y, $color, $text, $halign, $valign)
1279 {
1280 # Empty text case:
1281 if ($text === '') {
1282 if ($draw_it) return TRUE;
1283 return array(0, 0);
1284 }
1285
1286 # Calculate width and height offset factors using the alignment args:
1287 if ($valign == 'top') $v_factor = 0;
1288 elseif ($valign == 'center') $v_factor = 0.5;
1289 else $v_factor = 1.0; # 'bottom'
1290 if ($halign == 'left') $h_factor = 0;
1291 elseif ($halign == 'center') $h_factor = 0.5;
1292 else $h_factor = 1.0; # 'right'
1293
1294 if ($this->use_ttf) {
1295 return $this->ProcessTextTTF($draw_it, $font['font'], $font['size'],
1296 $angle, $x, $y, $color, $text, $h_factor, $v_factor);
1297 }
1298
1299 return $this->ProcessTextGD($draw_it, $font['font'], $font['width'], $font['height'],
1300 $angle, $x, $y, $color, $text, $h_factor, $v_factor);
1301 }
1302
1303
1304 /*
1305 * Draws a block of text. See comments above before ProcessText().
1306 * $which_font : PHPlot font array. (Contents depend on use_ttf flag).
1307 * $which_angle : Text angle in degrees
1308 * $which_xpos, $which_ypos: Reference point for the text
1309 * $which_color : GD color index to use for drawing the text
1310 * $which_text : The text to draw, with newlines (\n) between lines.
1311 * $which_halign : Horizontal (relative to the image) alignment: left, center, or right.
1312 * $which_valign : Vertical (relative to the image) alignment: top, center, or bottom.
1313 */
1314 function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text,
1315 $which_halign = 'left', $which_valign = 'bottom')
1316 {
1317 return $this->ProcessText(True,
1318 $which_font, $which_angle, $which_xpos, $which_ypos,
1319 $which_color, $which_text, $which_halign, $which_valign);
1320 }
1321
1322 /*
1323 * Returns the size of block of text. This is the orthogonal width and height of a bounding
1324 * box aligned with the X and Y axes of the text. Only for angle=0 is this the actual
1325 * width and height of the text block, but for any angle it is the amount of space needed
1326 * to contain the text.
1327 * $which_font : PHPlot font array. (Contents depend on use_ttf flag).
1328 * $which_angle : Text angle in degrees
1329 * $which_text : The text to draw, with newlines (\n) between lines.
1330 * Returns a two element array with: $width, $height.
1331 * This is just a wrapper for ProcessText() - see above.
1332 */
1333 function SizeText($which_font, $which_angle, $which_text)
1334 {
1335 // Color, position, and alignment are not used when calculating the size.
1336 return $this->ProcessText(False,
1337 $which_font, $which_angle, 0, 0, 1, $which_text, '', '');
1338 }
1339
1340
1344
1348 function SetFileFormat($format)
1349 {
1350 $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__);
1351 if (!$asked) return False;
1352 switch ($asked) {
1353 case 'jpg':
1354 $format_test = IMG_JPG;
1355 break;
1356 case 'png':
1357 $format_test = IMG_PNG;
1358 break;
1359 case 'gif':
1360 $format_test = IMG_GIF;
1361 break;
1362 case 'wbmp':
1363 $format_test = IMG_WBMP;
1364 break;
1365 }
1366 if (!(imagetypes() & $format_test)) {
1367 return $this->PrintError("SetFileFormat(): File format '$format' not supported");
1368 }
1369 $this->file_format = $asked;
1370 return TRUE;
1371 }
1372
1373
1380 function SetBgImage($input_file, $mode='centeredtile')
1381 {
1382 $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
1383 $this->bgimg = $input_file;
1384 return (boolean)$this->bgmode;
1385 }
1386
1393 function SetPlotAreaBgImage($input_file, $mode='tile')
1394 {
1395 $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
1396 $this->plotbgimg = $input_file;
1397 return (boolean)$this->plotbgmode;
1398 }
1399
1400
1404 function SetOutputFile($which_output_file)
1405 {
1406 $this->output_file = $which_output_file;
1407 return TRUE;
1408 }
1409
1414 function SetIsInline($which_ii)
1415 {
1416 $this->is_inline = (bool)$which_ii;
1417 return TRUE;
1418 }
1419
1420
1424 function PrintImage()
1425 {
1426 // Browser cache stuff submitted by Thiemo Nagel
1427 if ( (! $this->browser_cache) && (! $this->is_inline)) {
1428 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
1429 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
1430 header('Cache-Control: no-cache, must-revalidate');
1431 header('Pragma: no-cache');
1432 }
1433
1434 switch($this->file_format) {
1435 case 'png':
1436 if (! $this->is_inline) {
1437 Header('Content-type: image/png');
1438 }
1439 if ($this->is_inline && $this->output_file != '') {
1440 ImagePng($this->img, $this->output_file);
1441 } else {
1442 ImagePng($this->img);
1443 }
1444 break;
1445 case 'jpg':
1446 if (! $this->is_inline) {
1447 Header('Content-type: image/jpeg');
1448 }
1449 if ($this->is_inline && $this->output_file != '') {
1450 ImageJPEG($this->img, $this->output_file);
1451 } else {
1452 ImageJPEG($this->img);
1453 }
1454 break;
1455 case 'gif':
1456 if (! $this->is_inline) {
1457 Header('Content-type: image/gif');
1458 }
1459 if ($this->is_inline && $this->output_file != '') {
1460 ImageGIF($this->img, $this->output_file);
1461 } else {
1462 ImageGIF($this->img);
1463 }
1464
1465 break;
1466 case 'wbmp': // wireless bitmap, 2 bit.
1467 if (! $this->is_inline) {
1468 Header('Content-type: image/wbmp');
1469 }
1470 if ($this->is_inline && $this->output_file != '') {
1471 ImageWBMP($this->img, $this->output_file);
1472 } else {
1473 ImageWBMP($this->img);
1474 }
1475
1476 break;
1477 default:
1478 return $this->PrintError('PrintImage(): Please select an image type!');
1479 }
1480 return TRUE;
1481 }
1482
1506 function PrintError($error_message)
1507 {
1508 // Be sure not to loop recursively, e.g. PrintError - PrintImage - PrintError.
1509 if (isset($this->in_error)) return FALSE;
1510 $this->in_error = TRUE;
1511
1512 // Output an image containing the error message:
1513 if (!empty($this->img)) {
1514 $ypos = $this->image_height/2;
1515 $xpos = $this->image_width/2;
1516 $bgcolor = ImageColorResolve($this->img, 255, 255, 255);
1517 $fgcolor = ImageColorResolve($this->img, 0, 0, 0);
1518 ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, $bgcolor);
1519
1520 // Switch to built-in fonts, in case of error with TrueType fonts:
1521 $this->SetUseTTF(FALSE);
1522
1523 $this->DrawText($this->generic_font, 0, $xpos, $ypos, $fgcolor,
1524 wordwrap($error_message), 'center', 'center');
1525
1526 $this->PrintImage();
1527 } elseif (! $this->is_inline) {
1528 Header('HTTP/1.0 500 Internal Server Error');
1529 }
1530 trigger_error($error_message, E_USER_ERROR);
1531 unset($this->in_error);
1532 return FALSE; # In case error handler returns, rather than doing exit().
1533 }
1534
1541 function DrawError($error_message, $where_x = NULL, $where_y = NULL)
1542 {
1543 return $this->PrintError($error_message);
1544 }
1545
1549
1550
1554 function SetXDataLabelPos($which_xdlp)
1555 {
1556 $which_xdlp = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none',
1557 __FUNCTION__);
1558 if (!$which_xdlp) return FALSE;
1559 $this->x_data_label_pos = $which_xdlp;
1560 if ($this->x_data_label_pos != 'none')
1561 $this->x_tick_label_pos = 'none';
1562
1563 return TRUE;
1564 }
1565
1571 function SetYDataLabelPos($which_ydlp, $which_distance_from_point=0)
1572 {
1573 $which_ydlp = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, yaxis, all, plotin, none',
1574 __FUNCTION__);
1575 if (!$which_ydlp) return FALSE;
1576 $this->y_data_label_pos = $which_ydlp;
1577 //This bit in SetYDataLabelPos about plotleft is for those who were
1578 //using this function to set SetYTickLabelPos.
1579 if ( ($which_ydlp == 'plotleft') || ($which_ydlp == 'plotright') ||
1580 ($which_ydlp == 'both') || ($which_ydlp == 'yaxis') ) {
1581
1582 //Call sety_TICK_labelpos instead of sety_DATA_labelpos
1583 $this->SetYTickLabelPos($which_ydlp);
1584
1585 } elseif ($which_ydlp != 'none') {
1586 //right now its plotin or none
1587 $this->y_data_label_pos = 'plotin';
1588 }
1589
1590 return TRUE;
1591 }
1592
1593
1597 function SetXTickLabelPos($which_xtlp)
1598 {
1599 $which_xtlp = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none',
1600 __FUNCTION__);
1601 if (!$which_xtlp) return FALSE;
1602 $this->x_tick_label_pos = $which_xtlp;
1603 if ($which_xtlp != 'none')
1604 $this->x_data_label_pos = 'none';
1605
1606 return TRUE;
1607 }
1608
1612 function SetYTickLabelPos($which_ytlp)
1613 {
1614 $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none',
1615 __FUNCTION__);
1616 return (boolean)$this->y_tick_label_pos;
1617 }
1618
1623 function SetXLabelType($which_xlt)
1624 {
1625 $this->x_label_type = $this->CheckOption($which_xlt, 'data, time, title', __FUNCTION__);
1626 return (boolean)$this->x_label_type;
1627 }
1628
1632 function SetYLabelType($which_ylt)
1633 {
1634 $this->y_label_type = $this->CheckOption($which_ylt, 'data, time', __FUNCTION__);
1635 return (boolean)$this->y_label_type;
1636 }
1637
1638 function SetXTimeFormat($which_xtf)
1639 {
1640 $this->x_time_format = $which_xtf;
1641 return TRUE;
1642 }
1643
1644 function SetYTimeFormat($which_ytf)
1645 {
1646 $this->y_time_format = $which_ytf;
1647 return TRUE;
1648 }
1649
1650 function SetNumberFormat($decimal_point, $thousands_sep)
1651 {
1652 $this->decimal_point = $decimal_point;
1653 $this->thousands_sep = $thousands_sep;
1654 return TRUE;
1655 }
1656
1657
1658 function SetXLabelAngle($which_xla)
1659 {
1660 $this->x_label_angle = $which_xla;
1661 return TRUE;
1662 }
1663
1664 function SetYLabelAngle($which_yla)
1665 {
1666 $this->y_label_angle = $which_yla;
1667 return TRUE;
1668 }
1669
1673
1683 function CheckOption($which_opt, $which_acc, $which_func)
1684 {
1685 $asked = strtolower(trim($which_opt));
1686
1687 # Look for the supplied value in a comma/space separated list.
1688 if (strpos(", $which_acc,", ", $asked,") !== False)
1689 return $asked;
1690
1691 $this->PrintError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
1692 return NULL;
1693 }
1694
1695
1699 function SetBrowserCache($which_browser_cache)
1700 {
1701 $this->browser_cache = $which_browser_cache;
1702 return TRUE;
1703 }
1704
1708 function SetPrintImage($which_pi)
1709 {
1710 $this->print_image = $which_pi;
1711 return TRUE;
1712 }
1713
1717 function SetLegend($which_leg)
1718 {
1719 if (is_array($which_leg)) { // use array
1720 $this->legend = $which_leg;
1721 } elseif (! is_null($which_leg)) { // append string
1722 $this->legend[] = $which_leg;
1723 } else {
1724 return $this->PrintError("SetLegend(): argument must not be null.");
1725 }
1726 return TRUE;
1727 }
1728
1733 function SetLegendPixels($which_x, $which_y)
1734 {
1735 $this->legend_x_pos = $which_x;
1736 $this->legend_y_pos = $which_y;
1737 // Make sure this is unset, meaning we have pixel coords:
1738 unset($this->legend_xy_world);
1739
1740 return TRUE;
1741 }
1742
1750 function SetLegendWorld($which_x, $which_y)
1751 {
1752 $this->legend_x_pos = $which_x;
1753 $this->legend_y_pos = $which_y;
1754 $this->legend_xy_world = True;
1755
1756 return TRUE;
1757 }
1758
1759 /*
1760 * Set legend text alignment, color box alignment, and style options
1761 * $text_align accepts 'left' or 'right'.
1762 * $colorbox_align accepts 'left', 'right', 'none', or missing/empty. If missing or empty,
1763 * the same alignment as $text_align is used. Color box is positioned first.
1764 * $style is reserved for future use.
1765 */
1766 function SetLegendStyle($text_align, $colorbox_align = '', $style = '')
1767 {
1768 $this->legend_text_align = $this->CheckOption($text_align, 'left, right', __FUNCTION__);
1769 if (empty($colorbox_align))
1770 $this->legend_colorbox_align = $this->legend_text_align;
1771 else
1772 $this->legend_colorbox_align = $this->CheckOption($colorbox_align, 'left, right, none', __FUNCTION__);
1773 return ((boolean)$this->legend_text_align && (boolean)$this->legend_colorbox_align);
1774 }
1775
1779 function SetPlotBorderType($pbt)
1780 {
1781 $this->plot_border_type = $this->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__);
1782 return (boolean)$this->plot_border_type;
1783 }
1784
1788 function SetImageBorderType($sibt)
1789 {
1790 $this->image_border_type = $this->CheckOption($sibt, 'raised, plain', __FUNCTION__);
1791 return (boolean)$this->image_border_type;
1792 }
1793
1794
1799 {
1800 $this->draw_plot_area_background = (bool)$dpab;
1801 return TRUE;
1802 }
1803
1804
1808 function SetDrawYGrid($dyg)
1809 {
1810 $this->draw_y_grid = (bool)$dyg;
1811 return TRUE;
1812 }
1813
1814
1818 function SetDrawXGrid($dxg)
1819 {
1820 $this->draw_x_grid = (bool)$dxg;
1821 return TRUE;
1822 }
1823
1824
1828 function SetDrawDashedGrid($ddg)
1829 {
1830 $this->dashed_grid = (bool)$ddg;
1831 return TRUE;
1832 }
1833
1834
1839 {
1840 $this->draw_x_data_label_lines = (bool)$dxdl;
1841 return TRUE;
1842 }
1843
1844
1850 {
1851 $this->draw_y_data_label_lines = $dydl;
1852 return TRUE;
1853 }
1854
1859 function SetTitle($which_title)
1860 {
1861 $this->title_txt = $which_title;
1862 return TRUE;
1863 }
1864
1868 function SetXTitle($which_xtitle, $which_xpos = 'plotdown')
1869 {
1870 if ($which_xtitle == '')
1871 $which_xpos = 'none';
1872
1873 $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__);
1874 if (!$this->x_title_pos) return FALSE;
1875 $this->x_title_txt = $which_xtitle;
1876 return TRUE;
1877 }
1878
1879
1883 function SetYTitle($which_ytitle, $which_ypos = 'plotleft')
1884 {
1885 if ($which_ytitle == '')
1886 $which_ypos = 'none';
1887
1888 $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__);
1889 if (!$this->y_title_pos) return FALSE;
1890 $this->y_title_txt = $which_ytitle;
1891 return TRUE;
1892 }
1893
1898 function SetShading($which_s)
1899 {
1900 $this->shading = (int)$which_s;
1901 return TRUE;
1902 }
1903
1904 function SetPlotType($which_pt)
1905 {
1906 $this->plot_type = $this->CheckOption($which_pt,
1907 'bars, stackedbars, lines, linepoints, area, points, pie, thinbarline, squared',
1908 __FUNCTION__);
1909 return (boolean)$this->plot_type;
1910 }
1911
1916 function SetYAxisPosition($pos)
1917 {
1918 $this->y_axis_position = (int)$pos;
1919 return TRUE;
1920 }
1921
1926 function SetXAxisPosition($pos)
1927 {
1928 $this->x_axis_position = (int)$pos;
1929 return TRUE;
1930 }
1931
1932
1933 function SetXScaleType($which_xst)
1934 {
1935 $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__);
1936 return (boolean)$this->xscale_type;
1937 }
1938
1939 function SetYScaleType($which_yst)
1940 {
1941 $this->yscale_type = $this->CheckOption($which_yst, 'linear, log', __FUNCTION__);
1942 return (boolean)$this->yscale_type;
1943 }
1944
1945 function SetPrecisionX($which_prec)
1946 {
1947 $this->x_precision = $which_prec;
1948 $this->SetXLabelType('data');
1949 return TRUE;
1950 }
1951
1952 function SetPrecisionY($which_prec)
1953 {
1954 $this->y_precision = $which_prec;
1955 $this->SetYLabelType('data');
1956 return TRUE;
1957 }
1958
1959 function SetErrorBarLineWidth($which_seblw)
1960 {
1961 $this->error_bar_line_width = $which_seblw;
1962 return TRUE;
1963 }
1964
1965 function SetLabelScalePosition($which_blp)
1966 {
1967 //0 to 1
1968 $this->label_scale_position = $which_blp;
1969 return TRUE;
1970 }
1971
1972 function SetErrorBarSize($which_ebs)
1973 {
1974 //in pixels
1975 $this->error_bar_size = $which_ebs;
1976 return TRUE;
1977 }
1978
1982 function SetErrorBarShape($which_ebs)
1983 {
1984 $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__);
1985 return (boolean)$this->error_bar_shape;
1986 }
1987
1993 function SetPointShapes($which_pt)
1994 {
1995 if (is_null($which_pt)) {
1996 // Do nothing, use default value.
1997 } else if (is_array($which_pt)) {
1998 // Did we get an array with point shapes?
1999 $this->point_shapes = $which_pt;
2000 } else {
2001 // Single value into array
2002 $this->point_shapes = array($which_pt);
2003 }
2004
2005 foreach ($this->point_shapes as $shape)
2006 {
2007 if (!$this->CheckOption($shape,
2008 'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid, none',
2009 __FUNCTION__))
2010 return FALSE;
2011 }
2012
2013 // Make both point_shapes and point_sizes same size.
2014 $ps = count($this->point_sizes);
2015 $pt = count($this->point_shapes);
2016
2017 if ($ps < $pt) {
2018 $this->pad_array($this->point_sizes, $pt);
2019 } else if ($pt > $ps) {
2020 $this->pad_array($this->point_shapes, $ps);
2021 }
2022 return TRUE;
2023 }
2024
2030 function SetPointSizes($which_ps)
2031 {
2032 if (is_null($which_ps)) {
2033 // Do nothing, use default value.
2034 } else if (is_array($which_ps)) {
2035 // Did we get an array with point sizes?
2036 $this->point_sizes = $which_ps;
2037 } else {
2038 // Single value into array
2039 $this->point_sizes = array($which_ps);
2040 }
2041
2042 // Make both point_shapes and point_sizes same size.
2043 $ps = count($this->point_sizes);
2044 $pt = count($this->point_shapes);
2045
2046 if ($ps < $pt) {
2047 $this->pad_array($this->point_sizes, $pt);
2048 } else if ($pt > $ps) {
2049 $this->pad_array($this->point_shapes, $ps);
2050 }
2051
2052 // Fix odd point sizes for point shapes which need it
2053 for ($i = 0; $i < $pt; $i++) {
2054 if ($this->point_shapes[$i] == 'diamond' or $this->point_shapes[$i] == 'triangle') {
2055 if ($this->point_sizes[$i] % 2 != 0) {
2056 $this->point_sizes[$i]++;
2057 }
2058 }
2059 }
2060 return TRUE;
2061 }
2062
2063
2069 {
2070 $this->draw_broken_lines = (bool)$bl;
2071 return TRUE;
2072 }
2073
2074
2081 function SetDataType($which_dt)
2082 {
2083 //The next four lines are for past compatibility.
2084 if ($which_dt == 'text-linear') { $which_dt = 'text-data'; }
2085 elseif ($which_dt == 'linear-linear') { $which_dt = 'data-data'; }
2086 elseif ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; }
2087 elseif ($which_dt == 'text-data-pie') { $which_dt = 'text-data-single'; }
2088
2089
2090 $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '.
2091 'data-data, data-data-error', __FUNCTION__);
2092 return (boolean)$this->data_type;
2093 }
2094
2100 function SetDataValues(&$which_dv)
2101 {
2102 $this->num_data_rows = count($which_dv);
2103 $this->total_records = 0; // Perform some useful calculations.
2104 $this->records_per_group = 1;
2105 for ($i = 0, $recs = 0; $i < $this->num_data_rows; $i++) {
2106 // Copy
2107 $this->data[$i] = array_values($which_dv[$i]); // convert to numerical indices.
2108
2109 // Compute some values
2110 $recs = count($this->data[$i]);
2111 $this->total_records += $recs;
2112
2113 if ($recs > $this->records_per_group)
2114 $this->records_per_group = $recs;
2115
2116 $this->num_recs[$i] = $recs;
2117 }
2118 return TRUE;
2119 }
2120
2126 function PadArrays()
2127 {
2128 $this->pad_array($this->line_widths, $this->records_per_group);
2129 $this->pad_array($this->line_styles, $this->records_per_group);
2130
2131 $this->pad_array($this->data_colors, $this->records_per_group);
2132 $this->pad_array($this->data_border_colors, $this->records_per_group);
2133 $this->pad_array($this->error_bar_colors, $this->records_per_group);
2134
2135 $this->SetDataColors();
2136 $this->SetDataBorderColors();
2137 $this->SetErrorBarColors();
2138
2139 return TRUE;
2140 }
2141
2150 function pad_array(&$arr, $size)
2151 {
2152 if (! is_array($arr)) {
2153 $arr = array($arr);
2154 }
2155 $n = count($arr);
2156 $base = 0;
2157 while ($n < $size) $arr[$n++] = $arr[$base++];
2158 }
2159
2160 /*
2161 * Format a floating-point number.
2162 * This is like PHP's number_format, but uses class variables for separators.
2163 * The separators will default to locale-specific values, if available.
2164 */
2165 function number_format($number, $decimals=0)
2166 {
2167 if (!isset($this->decimal_point) || !isset($this->thousands_sep)) {
2168 // Load locale-specific values from environment:
2169 @setlocale(LC_ALL, '');
2170 // Fetch locale settings:
2171 $locale = @localeconv();
2172 if (!empty($locale) && isset($locale['decimal_point']) &&
2173 isset($locale['thousands_sep'])) {
2174 $this->decimal_point = $locale['decimal_point'];
2175 $this->thousands_sep = $locale['thousands_sep'];
2176 } else {
2177 // Locale information not available.
2178 $this->decimal_point = '.';
2179 $this->thousands_sep = ',';
2180 }
2181 }
2182 return number_format($number, $decimals, $this->decimal_point, $this->thousands_sep);
2183 }
2184
2185 /*
2186 * Register a callback (hook) function
2187 * reason - A pre-defined name where a callback can be defined.
2188 * function - The name of a function to register for callback, or an instance/method
2189 * pair in an array (see 'callbacks' in the PHP reference manual).
2190 * arg - Optional argument to supply to the callback function when it is triggered.
2191 * (Often called "clientData")
2192 * Returns: True if the callback reason is valid, else False.
2193 */
2194 function SetCallback($reason, $function, $arg = NULL)
2195 {
2196 // Use array_key_exists because valid reason keys have NULL as value.
2197 if (!array_key_exists($reason, $this->callbacks))
2198 return False;
2199 $this->callbacks[$reason] = array($function, $arg);
2200 return True;
2201 }
2202
2203 /*
2204 * Return the name of a function registered for callback. See SetCallBack.
2205 * reason - A pre-defined name where a callback can be defined.
2206 * Returns the current callback function (name or array) for the given reason,
2207 * or False if there was no active callback or the reason is not valid.
2208 * Note you can safely test the return value with a simple 'if', as
2209 * no valid function name evaluates to false.
2210 */
2211 function GetCallback($reason)
2212 {
2213 if (isset($this->callbacks[$reason]))
2214 return $this->callbacks[$reason][0];
2215 return False;
2216 }
2217
2218 /*
2219 * Un-register (remove) a function registered for callback.
2220 * reason - A pre-defined name where a callback can be defined.
2221 * Returns: True if it was a valid callback reason, else False.
2222 * Note: Returns True whether or not there was a callback registered.
2223 */
2224 function RemoveCallback($reason)
2225 {
2226 if (!array_key_exists($reason, $this->callbacks))
2227 return False;
2228 $this->callbacks[$reason] = NULL;
2229 return True;
2230 }
2231
2232 /*
2233 * Invoke a callback, if one is registered.
2234 * Accepts a variable number of arguments >= 1:
2235 * reason : A string naming the callback.
2236 * ... : Zero or more additional arguments to be passed to the
2237 * callback function, after the passthru argument:
2238 * callback_function($image, $passthru, ...)
2239 * Returns: nothing.
2240 */
2241 function DoCallback() # Note: Variable arguments
2242 {
2243 $args = func_get_args();
2244 $reason = $args[0];
2245 if (!isset($this->callbacks[$reason]))
2246 return;
2247 list($function, $args[0]) = $this->callbacks[$reason];
2248 array_unshift($args, $this->img);
2249 # Now args[] looks like: img, passthru, extra args...
2250 call_user_func_array($function, $args);
2251 }
2252
2253
2257
2267 {
2268 // Set some default min and max values before running through the data
2269 switch ($this->data_type) {
2270 case 'text-data':
2271 case 'text-data-single':
2272 $minx = 0;
2273 $maxx = $this->num_data_rows - 1 ;
2274 $miny = $this->data[0][1];
2275 $maxy = $miny;
2276 break;
2277 default: //Everything else: data-data, etc, take first value
2278 $minx = $this->data[0][1];
2279 $maxx = $minx;
2280 $miny = $this->data[0][2];
2281 $maxy = $miny;
2282 break;
2283 }
2284
2285 $mine = 0; // Maximum value for the -error bar (assume error bars always > 0)
2286 $maxe = 0; // Maximum value for the +error bar (assume error bars always > 0)
2287
2288 if ($this->plot_type == 'stackedbars') {
2289 $maxmaxy = $minminy = 0;
2290 } else {
2291 $minminy = $miny;
2292 $maxmaxy = $maxy;
2293 }
2294
2295 // Process each row of data
2296 for ($i=0; $i < $this->num_data_rows; $i++) {
2297 $j = 1; // Skip over the data label for this row.
2298
2299 switch ($this->data_type) {
2300 case 'text-data': // Data is passed in as (title, y1, y2, y3, ...)
2301 case 'text-data-single': // This one is for some pie charts
2302 // $numrecs = @ count($this->data[$i]);
2303 $maxy = (double)$this->data[$i][$j++];
2304 if ($this->plot_type == 'stackedbars') {
2305 $miny = 0;
2306 } else {
2307 $miny = $maxy;
2308 }
2309 for (; $j < $this->num_recs[$i]; $j++) {
2310 $val = (double)$this->data[$i][$j];
2311 if ($this->plot_type == 'stackedbars') {
2312 $maxy += abs($val); // only positive values for the moment
2313 } else {
2314 if ($val > $maxy) $maxy = $val;
2315 if ($val < $miny) $miny = $val;
2316 }
2317 }
2318 break;
2319 case 'data-data': // Data is passed in as (title, x, y, y2, y3, ...)
2320 // X value:
2321 $val = (double)$this->data[$i][$j++];
2322 if ($val > $maxx) $maxx = $val;
2323 if ($val < $minx) $minx = $val;
2324
2325 $miny = $maxy = (double)$this->data[$i][$j++];
2326 // $numrecs = @ count($this->data[$i]);
2327 for (; $j < $this->num_recs[$i]; $j++) {
2328 $val = (double)$this->data[$i][$j];
2329 if ($val > $maxy) $maxy = $val;
2330 if ($val < $miny) $miny = $val;
2331 }
2332 break;
2333 case 'data-data-error': // Data is passed in as (title, x, y, err+, err-, y2, err2+, err2-,...)
2334 // X value:
2335 $val = (double)$this->data[$i][$j++];
2336 if ($val > $maxx) $maxx = $val;
2337 if ($val < $minx) $minx = $val;
2338
2339 $miny = $maxy = (double)$this->data[$i][$j];
2340 // $numrecs = @ count($this->data[$i]);
2341 for (; $j < $this->num_recs[$i];) {
2342 // Y value:
2343 $val = (double)$this->data[$i][$j++];
2344 if ($val > $maxy) $maxy = $val;
2345 if ($val < $miny) $miny = $val;
2346 // Error +:
2347 $val = (double)$this->data[$i][$j++];
2348 if ($val > $maxe) $maxe = $val;
2349 // Error -:
2350 $val = (double)$this->data[$i][$j++];
2351 if ($val > $mine) $mine = $val;
2352 }
2353 $maxy = $maxy + $maxe;
2354 $miny = $miny - $mine; // assume error bars are always > 0
2355 break;
2356 default:
2357 return $this->PrintError("FindDataLimits(): Unknown data type '$this->data_type'.");
2358 }
2359 // Remember this row's min and max Y values:
2360 $this->data_miny[$i] = $miny;
2361 $this->data_maxy[$i] = $maxy;
2362
2363 if ($miny < $minminy) $minminy = $miny; // global min
2364 if ($maxy > $maxmaxy) $maxmaxy = $maxy; // global max
2365 }
2366
2367 $this->min_x = $minx;
2368 $this->max_x = $maxx;
2369 $this->min_y = $minminy;
2370 $this->max_y = $maxmaxy;
2371
2372 if ($this->GetCallback('debug_scale')) {
2373 $this->DoCallback('debug_scale', __FUNCTION__, array(
2374 'min_x' => $this->min_x, 'min_y' => $this->min_y,
2375 'max_x' => $this->max_x, 'max_y' => $this->max_y));
2376 }
2377 return TRUE;
2378 }
2379
2419 function CalcMargins($maximize)
2420 {
2421 // This is the line-to-line or line-to-text spacing:
2422 $gap = $this->safe_margin;
2423
2424 // Minimum margin on each side. This reduces the chance that the
2425 // right-most tick label (for example) will run off the image edge
2426 // if there are no titles on that side.
2427 $min_margin = 3 * $gap;
2428
2429 // Calculate the title sizes:
2430 list($unused, $title_height) = $this->SizeText($this->title_font, 0, $this->title_txt);
2431 list($unused, $x_title_height) = $this->SizeText($this->x_title_font, 0, $this->x_title_txt);
2432 list($y_title_width, $unused) = $this->SizeText($this->y_title_font, 90, $this->y_title_txt);
2433
2434 // Special case for maximum area usage with no X/Y titles or labels, only main title:
2435 if ($maximize) {
2436 if (!$this->plot_margins_set) {
2437 $this->x_left_margin = $gap;
2438 $this->x_right_margin = $gap;
2439 $this->y_top_margin = $gap;
2440 $this->y_bot_margin = $gap;
2441 if ($title_height > 0)
2442 $this->y_top_margin += $title_height + $gap;
2443 }
2444 return TRUE;
2445 }
2446
2447 // Make local variables for these. (They get used a lot and I'm tired of this, this, this.)
2451 $x_tick_len = $this->x_tick_length;
2454 $y_tick_len = $this->y_tick_length;
2455
2457
2458 // Calculate maximum height of X tick or data labels, depending on which one is on.
2459 // It is possible for both to be on, but this is only valid if all the data labels are empty.
2460 if ($x_data_label_pos == 'none') {
2461 $x_label_height = 0;
2462 } else {
2463 $x_label_height = $this->CalcMaxDataLabelSize();
2464 }
2465 if ($x_tick_label_pos != 'none' &&
2466 ($height = $this->CalcMaxTickLabelSize('x')) > $x_label_height) {
2467 $x_label_height = $height;
2468 }
2469
2470 // If Y tick labels are on, calculate the width of the widest tick label:
2471 if ($y_tick_label_pos == 'none')
2472 $y_label_width = 0;
2473 else
2474 $y_label_width = $this->CalcMaxTickLabelSize('y');
2475
2477
2478 // For X/Y tick and label position of 'xaxis' or 'yaxis', determine if the axis happens to be
2479 // on an edge of a plot. If it is, we need to account for the margins there.
2480 if ($this->x_axis_position <= $this->plot_min_y)
2481 $x_axis_pos = 'bottom';
2482 elseif ($this->x_axis_position >= $this->plot_max_y)
2483 $x_axis_pos = 'top';
2484 else
2485 $x_axis_pos = 'none';
2486
2487 if ($this->y_axis_position <= $this->plot_min_x)
2488 $y_axis_pos = 'left';
2489 elseif ($this->y_axis_position >= $this->plot_max_x)
2490 $y_axis_pos = 'right';
2491 else
2492 $y_axis_pos = 'none';
2493
2494 // y_top_margin: Main title, Upper X title, X ticks and tick labels, and X data labels:
2495 // y_bot_margin: Lower title, ticks and tick labels, and data labels:
2496 $top_margin = $gap;
2497 $bot_margin = $gap;
2498 $this->x_title_top_offset = $gap;
2499 $this->x_title_bot_offset = $gap;
2500
2501 if ($title_height > 0)
2502 $top_margin += $title_height + $gap;
2503
2504 if ($x_title_height > 0) {
2505 $pos = $this->x_title_pos;
2506 if ($pos == 'plotup' || $pos == 'both')
2507 $top_margin += $x_title_height + $gap;
2508 if ($pos == 'plotdown' || $pos == 'both')
2509 $bot_margin += $x_title_height + $gap;
2510 }
2511
2512 if ($x_tick_label_pos == 'plotup' || $x_tick_label_pos == 'both'
2513 || $x_data_label_pos == 'plotup' || $x_data_label_pos == 'both'
2514 || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'top')) {
2515 // X Tick or Data Labels above the plot:
2516 $top_margin += $x_label_height + $gap;
2517 $this->x_title_top_offset += $x_label_height + $gap;
2518 }
2519 if ($x_tick_label_pos == 'plotdown' || $x_tick_label_pos == 'both'
2520 || $x_data_label_pos == 'plotdown' || $x_data_label_pos == 'both'
2521 || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'bottom')) {
2522 // X Tick or Data Labels below the plot:
2523 $bot_margin += $x_label_height + $gap;
2524 $this->x_title_bot_offset += $x_label_height + $gap;
2525 }
2526 if ($x_tick_pos == 'plotup' || $x_tick_pos == 'both'
2527 || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'top')) {
2528 // X Ticks above the plot:
2529 $top_margin += $x_tick_len;
2530 $this->x_label_top_offset = $x_tick_len + $gap;
2531 $this->x_title_top_offset += $x_tick_len;
2532 } else {
2533 // No X Ticks above the plot:
2534 $this->x_label_top_offset = $gap;
2535 }
2536 if ($x_tick_pos == 'plotdown' || $x_tick_pos == 'both'
2537 || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'bottom')) {
2538 // X Ticks below the plot:
2539 $bot_margin += $x_tick_len;
2540 $this->x_label_bot_offset = $x_tick_len + $gap;
2541 $this->x_title_bot_offset += $x_tick_len;
2542 } else {
2543 // No X Ticks below the plot:
2544 $this->x_label_bot_offset = $gap;
2545 }
2546 // Label offsets for on-axis ticks:
2547 if ($x_tick_pos == 'xaxis') {
2548 $this->x_label_axis_offset = $x_tick_len + $gap;
2549 } else {
2550 $this->x_label_axis_offset = $gap;
2551 }
2552
2553 // x_left_margin: Left Y title, Y ticks and tick labels:
2554 // x_right_margin: Right Y title, Y ticks and tick labels:
2555 $left_margin = $gap;
2556 $right_margin = $gap;
2557 $this->y_title_left_offset = $gap;
2558 $this->y_title_right_offset = $gap;
2559
2560 if ($y_title_width > 0) {
2561 $pos = $this->y_title_pos;
2562 if ($pos == 'plotleft' || $pos == 'both')
2563 $left_margin += $y_title_width + $gap;
2564 if ($pos == 'plotright' || $pos == 'both')
2565 $right_margin += $y_title_width + $gap;
2566 }
2567
2568 if ($y_tick_label_pos == 'plotleft' || $y_tick_label_pos == 'both'
2569 || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'left')) {
2570 // Y Tick Labels left of plot:
2571 $left_margin += $y_label_width + $gap;
2572 $this->y_title_left_offset += $y_label_width + $gap;
2573 }
2574 if ($y_tick_label_pos == 'plotright' || $y_tick_label_pos == 'both'
2575 || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'right')) {
2576 // Y Tick Labels right of plot:
2577 $right_margin += $y_label_width + $gap;
2578 $this->y_title_right_offset += $y_label_width + $gap;
2579 }
2580 if ($y_tick_pos == 'plotleft' || $y_tick_pos == 'both'
2581 || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'left')) {
2582 // Y Ticks left of plot:
2583 $left_margin += $y_tick_len;
2584 $this->y_label_left_offset = $y_tick_len + $gap;
2585 $this->y_title_left_offset += $y_tick_len;
2586 } else {
2587 // No Y Ticks left of plot:
2588 $this->y_label_left_offset = $gap;
2589 }
2590 if ($y_tick_pos == 'plotright' || $y_tick_pos == 'both'
2591 || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'right')) {
2592 // Y Ticks right of plot:
2593 $right_margin += $y_tick_len;
2594 $this->y_label_right_offset = $y_tick_len + $gap;
2595 $this->y_title_right_offset += $y_tick_len;
2596 } else {
2597 // No Y Ticks right of plot:
2598 $this->y_label_right_offset = $gap;
2599 }
2600
2601 // Label offsets for on-axis ticks:
2602 if ($x_tick_pos == 'yaxis') {
2603 $this->y_label_axis_offset = $y_tick_len + $gap;
2604 } else {
2605 $this->y_label_axis_offset = $gap;
2606 }
2607
2608 // Apply the minimum margins and store in the object.
2609 // Do not set margins if they are user-defined (see note at top of function).
2610 if (!$this->plot_margins_set) {
2611 $this->y_top_margin = max($min_margin, $top_margin);
2612 $this->y_bot_margin = max($min_margin, $bot_margin);
2613 $this->x_left_margin = max($min_margin, $left_margin);
2614 $this->x_right_margin = max($min_margin, $right_margin);
2615 }
2616
2617 if ($this->GetCallback('debug_scale')) {
2618 // (Too bad compact() doesn't work on class member variables...)
2619 $this->DoCallback('debug_scale', __FUNCTION__, array(
2620 'x_label_height' => $x_label_height,
2621 'y_label_width' => $y_label_width,
2622 'x_tick_len' => $x_tick_len,
2623 'y_tick_len' => $y_tick_len,
2624 'x_left_margin' => $this->x_left_margin,
2625 'x_right_margin' => $this->x_right_margin,
2626 'y_top_margin' => $this->y_top_margin,
2627 'y_bot_margin' => $this->y_bot_margin,
2628 'x_label_top_offset' => $this->x_label_top_offset,
2629 'x_label_bot_offset' => $this->x_label_bot_offset,
2630 'y_label_left_offset' => $this->y_label_left_offset,
2631 'y_label_right_offset' => $this->y_label_right_offset,
2632 'x_title_top_offset' => $this->x_title_top_offset,
2633 'x_title_bot_offset' => $this->x_title_bot_offset,
2634 'y_title_left_offset' => $this->y_title_left_offset,
2635 'y_title_right_offset' => $this->y_title_right_offset));
2636 }
2637
2638 return TRUE;
2639 }
2640
2641 /*
2642 * Calculate the plot area (device coordinates) from the margins.
2643 * (This used to be part of SetPlotAreaPixels.)
2644 * The margins might come from SetMarginsPixels, SetPlotAreaPixels,
2645 * or CalcMargins.
2646 */
2648 {
2649 $this->plot_area = array($this->x_left_margin, $this->y_top_margin,
2650 $this->image_width - $this->x_right_margin,
2651 $this->image_height - $this->y_bot_margin);
2652 $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
2653 $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
2654
2655 $this->DoCallback('debug_scale', __FUNCTION__, $this->plot_area);
2656 return TRUE;
2657 }
2658
2659
2665 function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm)
2666 {
2667 $this->x_left_margin = $which_lm;
2668 $this->x_right_margin = $which_rm;
2669 $this->y_top_margin = $which_tm;
2670 $this->y_bot_margin = $which_bm;
2671
2672 $this->plot_margins_set = TRUE;
2673
2674 return TRUE;
2675 }
2676
2687 function SetPlotAreaPixels($x1, $y1, $x2, $y2)
2688 {
2689 $this->x_left_margin = $x1;
2690 $this->x_right_margin = $this->image_width - $x2;
2691 $this->y_top_margin = $y1;
2692 $this->y_bot_margin = $this->image_height - $y2;
2693
2694 $this->plot_margins_set = TRUE;
2695
2696 return TRUE;
2697 }
2698
2699 /*
2700 * Calculate the World Coordinate limits of the plot area.
2701 * This goes with SetPlotAreaWorld, but the calculations are
2702 * deferred until the graph is being drawn.
2703 * Uses: plot_min_x, plot_max_x, plot_min_y, plot_max_y
2704 * which can be user-supplied or NULL to auto-calculate.
2705 * Pre-requisites: FindDataLimits()
2706 */
2708 {
2709 if (isset($this->plot_min_x) && $this->plot_min_x !== '')
2710 $xmin = $this->plot_min_x;
2711 elseif ($this->data_type == 'text-data') // Valid for data without X values only.
2712 $xmin = 0;
2713 else
2714 $xmin = $this->min_x;
2715
2716 if (isset($this->plot_max_x) && $this->plot_max_x !== '')
2717 $xmax = $this->plot_max_x;
2718 elseif ($this->data_type == 'text-data') // Valid for data without X values only.
2719 $xmax = $this->max_x + 1;
2720 else
2721 $xmax = $this->max_x;
2722
2723 // Leave room above and below the highest and lowest data points.
2724
2725 if (!isset($this->plot_min_y) || $this->plot_min_y === '')
2726 $ymin = floor($this->min_y - abs($this->min_y) * 0.1);
2727 else
2728 $ymin = $this->plot_min_y;
2729
2730 if (!isset($this->plot_max_y) || $this->plot_max_y === '')
2731 $ymax = ceil($this->max_y + abs($this->max_y) * 0.1);
2732 else
2733 $ymax = $this->plot_max_y;
2734
2735 // Error checking
2736
2737 if ($ymin == $ymax) // Minimum height
2738 $ymax += 1;
2739
2740 if ($this->yscale_type == 'log') {
2741 if ($ymin <= 0) {
2742 $ymin = 1;
2743 }
2744 if ($ymax <= 0) {
2745 // Note: Error messages reference the user function, not this function.
2746 return $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0');
2747 }
2748 }
2749
2750 if ($ymax <= $ymin) {
2751 return $this->PrintError('SetPlotAreaWorld(): Error in data - max not greater than min');
2752 }
2753
2754 $this->plot_min_x = $xmin;
2755 $this->plot_max_x = $xmax;
2756 $this->plot_min_y = $ymin;
2757 $this->plot_max_y = $ymax;
2758 if ($this->GetCallback('debug_scale')) {
2759 $this->DoCallback('debug_scale', __FUNCTION__, array(
2760 'plot_min_x' => $this->plot_min_x, 'plot_min_y' => $this->plot_min_y,
2761 'plot_max_x' => $this->plot_max_x, 'plot_max_y' => $this->plot_max_y));
2762 }
2763
2764 return TRUE;
2765 }
2766
2772 function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL)
2773 {
2774 $this->plot_min_x = $xmin;
2775 $this->plot_max_x = $xmax;
2776 $this->plot_min_y = $ymin;
2777 $this->plot_max_y = $ymax;
2778 return TRUE;
2779 } //function SetPlotAreaWorld
2780
2781
2785 function CalcBarWidths()
2786 {
2787 // group_width is the width of a group, including padding
2788 $group_width = $this->plot_area_width / $this->num_data_rows;
2789
2790 // Actual number of bar spaces in the group. This includes the drawn bars, and
2791 // 'bar_extra_space'-worth of extra bars.
2792 // Note that 'records_per_group' includes the label, so subtract one to get
2793 // the number of points in the group. 'stackedbars' have 1 bar space per group.
2794 if ($this->plot_type == 'stackedbars') {
2795 $num_spots = 1 + $this->bar_extra_space;
2796 } else {
2797 $num_spots = $this->records_per_group - 1 + $this->bar_extra_space;
2798 }
2799
2800 // record_bar_width is the width of each bar's allocated area.
2801 // If bar_width_adjust=1 this is the width of the bar, otherwise
2802 // the bar is centered inside record_bar_width.
2803 // The equation is:
2804 // group_frac_width * group_width = record_bar_width * num_spots
2805 $this->record_bar_width = $this->group_frac_width * $group_width / $num_spots;
2806
2807 // Note that the extra space due to group_frac_width and bar_extra_space will be
2808 // evenly divided on each side of the group: the drawn bars are centered in the group.
2809
2810 // Within each bar's allocated space, if bar_width_adjust=1 the bar fills the
2811 // space, otherwise it is centered.
2812 // This is the actual drawn bar width:
2813 $this->actual_bar_width = $this->record_bar_width * $this->bar_width_adjust;
2814 // This is the gap on each side of the bar (0 if bar_width_adjust=1):
2815 $this->bar_adjust_gap = ($this->record_bar_width - $this->actual_bar_width) / 2;
2816
2817 return TRUE;
2818 }
2819
2820 /*
2821 * Calculate X and Y Axis Positions, world coordinates.
2822 * This needs the min/max x/y range set by CalcPlotAreaWorld.
2823 * It adjusts or sets x_axis_position and y_axis_position per the data.
2824 * Empty string means the values need to be calculated; otherwise they
2825 * are supplied but need to be validated against the World area.
2826 *
2827 * Note: This used to be in CalcTranslation, but CalcMargins needs it too.
2828 * This does not calculate the pixel values of the axes. That happens in
2829 * CalcTranslation, after scaling is set up (which has to happen after
2830 * margins are set up).
2831 */
2833 {
2834 // If no user-provided Y axis position, default to axis on left side.
2835 // Otherwise, make sure user-provided position is inside the plot area.
2836 if ($this->y_axis_position === '')
2837 $this->y_axis_position = $this->plot_min_x;
2838 else
2839 $this->y_axis_position = min(max($this->plot_min_x, $this->y_axis_position), $this->plot_max_x);
2840
2841 // If no user-provided X axis position, default to axis at Y=0 (if in range), or min_y
2842 // if the range does not include 0, or 1 for log plots.
2843 // Otherwise, make sure user-provided position is inside the plot area.
2844 if ($this->x_axis_position === '') {
2845 if ($this->yscale_type == 'log')
2846 $this->x_axis_position = 1;
2847 elseif ($this->plot_min_y <= 0 && 0 <= $this->plot_max_y)
2848 $this->x_axis_position = 0;
2849 else
2850 $this->x_axis_position = $this->plot_min_y;
2851 } else
2852 $this->x_axis_position = min(max($this->plot_min_y, $this->x_axis_position), $this->plot_max_y);
2853
2854 if ($this->GetCallback('debug_scale')) {
2855 $this->DoCallback('debug_scale', __FUNCTION__, array(
2856 'x_axis_position' => $this->x_axis_position,
2857 'y_axis_position' => $this->y_axis_position));
2858 }
2859
2860 return TRUE;
2861 }
2862
2867 {
2868 if ($this->plot_max_x - $this->plot_min_x == 0) { // Check for div by 0
2869 $this->xscale = 0;
2870 } else {
2871 if ($this->xscale_type == 'log') {
2872 $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x));
2873 } else {
2874 $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x);
2875 }
2876 }
2877
2878 if ($this->plot_max_y - $this->plot_min_y == 0) { // Check for div by 0
2879 $this->yscale = 0;
2880 } else {
2881 if ($this->yscale_type == 'log') {
2882 $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y));
2883 } else {
2884 $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y);
2885 }
2886 }
2887 // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively
2888 if ($this->xscale_type == 'log') {
2889 $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) );
2890 } else {
2891 $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x);
2892 }
2893 if ($this->yscale_type == 'log') {
2894 $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y));
2895 } else {
2896 $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y);
2897 }
2898
2899 // Convert axis positions to device coordinates:
2900 $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
2901 $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
2902
2903 if ($this->GetCallback('debug_scale')) {
2904 $this->DoCallback('debug_scale', __FUNCTION__, array(
2905 'xscale' => $this->xscale, 'yscale' => $this->yscale,
2906 'plot_origin_x' => $this->plot_origin_x, 'plot_origin_y' => $this->plot_origin_y,
2907 'y_axis_x_pixels' => $this->y_axis_x_pixels,
2908 'x_axis_y_pixels' => $this->x_axis_y_pixels));
2909 }
2910
2911 return TRUE;
2912 } // function CalcTranslation()
2913
2914
2919 function xtr($x_world)
2920 {
2921 if ($this->xscale_type == 'log') {
2922 $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ;
2923 } else {
2924 $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ;
2925 }
2926 return round($x_pixels);
2927 }
2928
2929
2934 function ytr($y_world)
2935 {
2936 if ($this->yscale_type == 'log') {
2937 //minus because GD defines y = 0 at top. doh!
2938 $y_pixels = $this->plot_origin_y - log10($y_world) * $this->yscale ;
2939 } else {
2940 $y_pixels = $this->plot_origin_y - $y_world * $this->yscale ;
2941 }
2942 return round($y_pixels);
2943 }
2944
2945 /*
2946 * Calculate tick parameters: Start, end, and delta values. This is used
2947 * by both DrawXTicks() and DrawYTicks().
2948 * This currently uses the same simplistic method previously used by
2949 * PHPlot (basically just range/10), but splitting this out into its
2950 * own function is the first step in replacing the method.
2951 * This is also used by CalcMaxTickSize() for CalcMargins().
2952 *
2953 * $which : 'x' or 'y' : Which tick parameters to calculate
2954 *
2955 * Returns an array of 3 elements: tick_start, tick_end, tick_step
2956 */
2957 function CalcTicks($which)
2958 {
2959 if ($which == 'x') {
2960 $num_ticks = $this->num_x_ticks;
2961 $tick_inc = $this->x_tick_inc;
2962 $data_max = $this->plot_max_x;
2963 $data_min = $this->plot_min_x;
2964 $skip_lo = $this->skip_left_tick;
2965 $skip_hi = $this->skip_right_tick;
2966 } elseif ($which == 'y') {
2967 $num_ticks = $this->num_y_ticks;
2968 $tick_inc = $this->y_tick_inc;
2969 $data_max = $this->plot_max_y;
2970 $data_min = $this->plot_min_y;
2971 $skip_lo = $this->skip_bottom_tick;
2972 $skip_hi = $this->skip_top_tick;
2973 } else {
2974 return $this->PrintError("CalcTicks: Invalid usage ($which)");
2975 }
2976
2977 if (!empty($tick_inc)) {
2978 $tick_step = $tick_inc;
2979 } elseif (!empty($num_ticks)) {
2980 $tick_step = ($data_max - $data_min) / $num_ticks;
2981 } else {
2982 $tick_step = ($data_max - $data_min) / 10;
2983 }
2984
2985 // NOTE: When working with floats, because of approximations when adding $tick_step,
2986 // the value may not quite reach the end, or may exceed it very slightly.
2987 // So apply a "fudge" factor.
2988 $tick_start = (double)$data_min;
2989 $tick_end = (double)$data_max + ($data_max - $data_min) / 10000.0;
2990
2991 if ($skip_lo)
2992 $tick_start += $tick_step;
2993
2994 if ($skip_hi)
2995 $tick_end -= $tick_step;
2996
2997 return array($tick_start, $tick_end, $tick_step);
2998 }
2999
3000 /*
3001 * Calculate the size of the biggest tick label. This is used by CalcMargins().
3002 * For 'x' ticks, it returns the height . For 'y' ticks, it returns the width.
3003 * This means height along Y, or width along X - not relative to the text angle.
3004 * That is what we need to calculate the needed margin space.
3005 * (Previous versions of PHPlot estimated this, using the maximum X or Y value,
3006 * or maybe the longest string. That doesn't work. -10 is longer than 9, etc.
3007 * So this gets the actual size of each label, slow as that may be.
3008 */
3009 function CalcMaxTickLabelSize($which)
3010 {
3011 list($tick_val, $tick_end, $tick_step) = $this->CalcTicks($which);
3012
3013 if ($which == 'x') {
3014 $font = $this->x_label_font;
3015 $angle = $this->x_label_angle;
3016 } elseif ($which == 'y') {
3017 $font = $this->y_label_font;
3018 $angle = $this->y_label_angle;
3019 } else {
3020 return $this->PrintError("CalcMaxTickLabelSize: Invalid usage ($which)");
3021 }
3022
3023 $max_width = 0;
3024 $max_height = 0;
3025
3026 // Loop over ticks, same as DrawXTicks and DrawYTicks:
3027 while ($tick_val <= $tick_end) {
3028 $tick_label = $this->FormatLabel($which, $tick_val);
3029 list($width, $height) = $this->SizeText($font, $angle, $tick_label);
3030 if ($width > $max_width) $max_width = $width;
3031 if ($height > $max_height) $max_height = $height;
3032 $tick_val += $tick_step;
3033 }
3034 if ($this->GetCallback('debug_scale')) {
3035 $this->DoCallback('debug_scale', __FUNCTION__, array(
3036 'which' => $which, 'height' => $max_height, 'width' => $max_width));
3037 }
3038
3039 if ($which == 'x')
3040 return $max_height;
3041 return $max_width;
3042 }
3043
3044 /*
3045 * Calculate the size of the biggest X data label. This is used by CalcMargins().
3046 * Returns the height along Y axis of the biggest X data label.
3047 * (This calculates width and height, but only height is used at present.)
3048 */
3050 {
3051 $font = $this->x_label_font;
3052 $angle = $this->x_label_angle;
3053 $max_width = 0;
3054 $max_height = 0;
3055
3056 // Loop over all data labels and find the biggest:
3057 for ($i = 0; $i < $this->num_data_rows; $i++) {
3058 $label = $this->FormatLabel('x', $this->data[$i][0]);
3059 list($width, $height) = $this->SizeText($font, $angle, $label);
3060 if ($width > $max_width) $max_width = $width;
3061 if ($height > $max_height) $max_height = $height;
3062 }
3063 if ($this->GetCallback('debug_scale')) {
3064 $this->DoCallback('debug_scale', __FUNCTION__, array(
3065 'height' => $max_height, 'width' => $max_width));
3066 }
3067
3068 return $max_height;
3069 }
3070
3077 function FormatLabel($which_pos, $which_lab)
3078 {
3079 $lab = $which_lab; // Default to no formatting.
3080 if ($lab !== '') { // Don't format empty strings (especially as time or numbers)
3081 if ($which_pos == 'x') {
3082 switch ($this->x_label_type) {
3083 case 'title': // Note: This is obsolete
3084 $lab = @ $this->data[$which_lab][0];
3085 break;
3086 case 'data':
3087 $lab = $this->number_format($which_lab, $this->x_precision).$this->data_units_text;
3088 break;
3089 case 'time':
3090 $lab = strftime($this->x_time_format, $which_lab);
3091 break;
3092 }
3093 } elseif ($which_pos == 'y') {
3094 switch ($this->y_label_type) {
3095 case 'data':
3096 $lab = $this->number_format($which_lab, $this->y_precision).$this->data_units_text;
3097 break;
3098 case 'time':
3099 $lab = strftime($this->y_time_format, $which_lab);
3100 break;
3101 }
3102 }
3103 }
3104 return $lab;
3105 } //function FormatLabel
3106
3107
3108
3112
3116 function SetXTickIncrement($which_ti='')
3117 {
3118 $this->x_tick_inc = $which_ti;
3119 if (!empty($which_ti)) {
3120 $this->num_x_ticks = ''; //either use num_x_ticks or x_tick_inc, not both
3121 }
3122 return TRUE;
3123 }
3124
3128 function SetYTickIncrement($which_ti='')
3129 {
3130 $this->y_tick_inc = $which_ti;
3131 if (!empty($which_ti)) {
3132 $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
3133 }
3134 return TRUE;
3135 }
3136
3137
3138 function SetNumXTicks($which_nt)
3139 {
3140 $this->num_x_ticks = $which_nt;
3141 if (!empty($which_nt)) {
3142 $this->x_tick_inc = ''; //either use num_x_ticks or x_tick_inc, not both
3143 }
3144 return TRUE;
3145 }
3146
3147 function SetNumYTicks($which_nt)
3148 {
3149 $this->num_y_ticks = $which_nt;
3150 if (!empty($which_nt)) {
3151 $this->y_tick_inc = ''; //either use num_y_ticks or y_tick_inc, not both
3152 }
3153 return TRUE;
3154 }
3155
3159 function SetYTickPos($which_tp)
3160 {
3161 $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', __FUNCTION__);
3162 return (boolean)$this->y_tick_pos;
3163 }
3167 function SetXTickPos($which_tp)
3168 {
3169 $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__);
3170 return (boolean)$this->x_tick_pos;
3171 }
3172
3176 function SetSkipTopTick($skip)
3177 {
3178 $this->skip_top_tick = (bool)$skip;
3179 return TRUE;
3180 }
3181
3185 function SetSkipBottomTick($skip)
3186 {
3187 $this->skip_bottom_tick = (bool)$skip;
3188 return TRUE;
3189 }
3190
3194 function SetSkipLeftTick($skip)
3195 {
3196 $this->skip_left_tick = (bool)$skip;
3197 return TRUE;
3198 }
3199
3203 function SetSkipRightTick($skip)
3204 {
3205 $this->skip_right_tick = (bool)$skip;
3206 return TRUE;
3207 }
3208
3209 function SetXTickLength($which_xln)
3210 {
3211 $this->x_tick_length = $which_xln;
3212 return TRUE;
3213 }
3214
3215 function SetYTickLength($which_yln)
3216 {
3217 $this->y_tick_length = $which_yln;
3218 return TRUE;
3219 }
3220
3221 function SetXTickCrossing($which_xc)
3222 {
3223 $this->x_tick_cross = $which_xc;
3224 return TRUE;
3225 }
3226
3227 function SetYTickCrossing($which_yc)
3228 {
3229 $this->y_tick_cross = $which_yc;
3230 return TRUE;
3231 }
3232
3233
3237
3242 {
3243 // Don't draw this twice if drawing two plots on one image
3244 if (! $this->background_done) {
3245 if (isset($this->bgimg)) { // If bgimg is defined, use it
3246 $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode);
3247 } else { // Else use solid color
3248 ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height,
3249 $this->ndx_bg_color);
3250 }
3251 $this->background_done = TRUE;
3252 }
3253 return TRUE;
3254 }
3255
3256
3261 {
3262 if (isset($this->plotbgimg)) {
3263 $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1],
3264 $this->plot_area_width, $this->plot_area_height, $this->plotbgmode);
3265 }
3266 else {
3267 if ($this->draw_plot_area_background) {
3268 ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1],
3269 $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color);
3270 }
3271 }
3272
3273 return TRUE;
3274 }
3275
3276
3287 function tile_img($file, $xorig, $yorig, $width, $height, $mode)
3288 {
3289 $im = $this->GetImage($file, $tile_width, $tile_height);
3290 if (!$im)
3291 return FALSE; // GetImage already produced an error message.
3292
3293 if ($mode == 'scale') {
3294 imagecopyresized($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, $tile_width, $tile_height);
3295 return TRUE;
3296 } else if ($mode == 'centeredtile') {
3297 $x0 = - floor($tile_width/2); // Make the tile look better
3298 $y0 = - floor($tile_height/2);
3299 } else if ($mode = 'tile') {
3300 $x0 = 0;
3301 $y0 = 0;
3302 }
3303
3304 // Actually draw the tile
3305
3306 // But first on a temporal image.
3307 $tmp = ImageCreate($width, $height);
3308 if (! $tmp)
3309 return $this->PrintError('tile_img(): Could not create image resource.');
3310
3311 for ($x = $x0; $x < $width; $x += $tile_width)
3312 for ($y = $y0; $y < $height; $y += $tile_height)
3313 imagecopy($tmp, $im, $x, $y, 0, 0, $tile_width, $tile_height);
3314
3315 // Copy the temporal image onto the final one.
3316 imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height);
3317
3318 // Free resources
3319 imagedestroy($tmp);
3320 imagedestroy($im);
3321
3322 return TRUE;
3323 } // function tile_img
3324
3325
3330 {
3331 switch ($this->image_border_type) {
3332 case 'raised':
3333 ImageLine($this->img, 0, 0, $this->image_width-1, 0, $this->ndx_i_border);
3334 ImageLine($this->img, 1, 1, $this->image_width-2, 1, $this->ndx_i_border);
3335 ImageLine($this->img, 0, 0, 0, $this->image_height-1, $this->ndx_i_border);
3336 ImageLine($this->img, 1, 1, 1, $this->image_height-2, $this->ndx_i_border);
3337 ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
3338 $this->image_height-1, $this->ndx_i_border_dark);
3339 ImageLine($this->img, 0, $this->image_height-1, $this->image_width-1,
3340 $this->image_height-1, $this->ndx_i_border_dark);
3341 ImageLine($this->img, $this->image_width-2, 1, $this->image_width-2,
3342 $this->image_height-2, $this->ndx_i_border_dark);
3343 ImageLine($this->img, 1, $this->image_height-2, $this->image_width-2,
3344 $this->image_height-2, $this->ndx_i_border_dark);
3345 break;
3346 case 'plain':
3347 ImageLine($this->img, 0, 0, $this->image_width-1, 0, $this->ndx_i_border_dark);
3348 ImageLine($this->img, $this->image_width-1, 0, $this->image_width-1,
3349 $this->image_height-1, $this->ndx_i_border_dark);
3350 ImageLine($this->img, $this->image_width-1, $this->image_height-1, 0, $this->image_height-1,
3351 $this->ndx_i_border_dark);
3352 ImageLine($this->img, 0, 0, 0, $this->image_height-1, $this->ndx_i_border_dark);
3353 break;
3354 case 'none':
3355 break;
3356 default:
3357 return $this->PrintError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'");
3358 }
3359 return TRUE;
3360 }
3361
3362
3366 function DrawTitle()
3367 {
3368 // Center of the plot area
3369 //$xpos = ($this->plot_area[0] + $this->plot_area_width )/ 2;
3370
3371 // Center of the image:
3372 $xpos = $this->image_width / 2;
3373
3374 // Place it at almost at the top
3375 $ypos = $this->safe_margin;
3376
3377 $this->DrawText($this->title_font, 0, $xpos, $ypos,
3378 $this->ndx_title_color, $this->title_txt, 'center', 'top');
3379
3380 return TRUE;
3381
3382 }
3383
3384
3388 function DrawXTitle()
3389 {
3390 if ($this->x_title_pos == 'none')
3391 return TRUE;
3392
3393 // Center of the plot
3394 $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2;
3395
3396 // Upper title
3397 if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') {
3398 $ypos = $this->plot_area[1] - $this->x_title_top_offset;
3399 $this->DrawText($this->x_title_font, 0, $xpos, $ypos, $this->ndx_title_color,
3400 $this->x_title_txt, 'center', 'bottom');
3401 }
3402 // Lower title
3403 if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') {
3404 $ypos = $this->plot_area[3] + $this->x_title_bot_offset;
3405 $this->DrawText($this->x_title_font, 0, $xpos, $ypos, $this->ndx_title_color,
3406 $this->x_title_txt, 'center', 'top');
3407 }
3408 return TRUE;
3409 }
3410
3414 function DrawYTitle()
3415 {
3416 if ($this->y_title_pos == 'none')
3417 return TRUE;
3418
3419 // Center the title vertically to the plot area
3420 $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2;
3421
3422 if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') {
3423 $xpos = $this->plot_area[0] - $this->y_title_left_offset;
3424 $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
3425 $this->y_title_txt, 'right', 'center');
3426 }
3427 if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') {
3428 $xpos = $this->plot_area[2] + $this->y_title_right_offset;
3429 $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
3430 $this->y_title_txt, 'left', 'center');
3431 }
3432
3433 return TRUE;
3434 }
3435
3436
3437 /*
3438 * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
3439 */
3440 function DrawYAxis()
3441 {
3442 // Draw ticks, labels and grid, if any
3443 $this->DrawYTicks();
3444
3445 // Draw Y axis at X = y_axis_x_pixels
3446 ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1],
3447 $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color);
3448
3449 return TRUE;
3450 }
3451
3452 /*
3453 *
3454 */
3455 function DrawXAxis()
3456 {
3457 // Draw ticks, labels and grid
3458 $this->DrawXTicks();
3459
3460 /* This tick and label tend to overlap with regular Y Axis labels,
3461 * as Mike Pullen pointed out.
3462 *
3463 //Draw Tick and Label for X axis
3464 if (! $this->skip_bottom_tick) {
3465 $ylab =$this->FormatLabel('y', $this->x_axis_position);
3466 $this->DrawYTick($ylab, $this->x_axis_y_pixels);
3467 }
3468 */
3469 //Draw X Axis at Y = x_axis_y_pixels
3470 ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels,
3471 $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color);
3472
3473 return TRUE;
3474 }
3475
3479 function DrawYTick($which_ylab, $which_ypix)
3480 {
3481 // Ticks on Y axis
3482 if ($this->y_tick_pos == 'yaxis') {
3483 ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix,
3484 $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
3485 }
3486
3487 // Ticks to the left of the Plot Area
3488 if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) {
3489 ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length, $which_ypix,
3490 $this->plot_area[0] + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
3491 }
3492
3493 // Ticks to the right of the Plot Area
3494 if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) {
3495 ImageLine($this->img, $this->plot_area[2] + $this->y_tick_length, $which_ypix,
3496 $this->plot_area[2] - $this->y_tick_cross, $which_ypix, $this->ndx_tick_color);
3497 }
3498
3499 // Labels on Y axis
3500 if ($this->y_tick_label_pos == 'yaxis') {
3501 $this->DrawText($this->y_label_font, $this->y_label_angle,
3502 $this->y_axis_x_pixels - $this->y_label_axis_offset, $which_ypix,
3503 $this->ndx_text_color, $which_ylab, 'right', 'center');
3504 }
3505
3506 // Labels to the left of the plot area
3507 if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') {
3508 $this->DrawText($this->y_label_font, $this->y_label_angle,
3509 $this->plot_area[0] - $this->y_label_left_offset, $which_ypix,
3510 $this->ndx_text_color, $which_ylab, 'right', 'center');
3511 }
3512 // Labels to the right of the plot area
3513 if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') {
3514 $this->DrawText($this->y_label_font, $this->y_label_angle,
3515 $this->plot_area[2] + $this->y_label_right_offset, $which_ypix,
3516 $this->ndx_text_color, $which_ylab, 'left', 'center');
3517 }
3518 return TRUE;
3519 } // Function DrawYTick()
3520
3521
3528 function DrawYTicks()
3529 {
3530 // Sets the line style for IMG_COLOR_STYLED lines (grid)
3531 if ($this->dashed_grid) {
3532 $this->SetDashedStyle($this->ndx_light_grid_color);
3533 $style = IMG_COLOR_STYLED;
3534 } else {
3535 $style = $this->ndx_light_grid_color;
3536 }
3537
3538 // Calculate the tick start, end, and step:
3539 list($y_tmp, $y_end, $delta_y) = $this->CalcTicks('y');
3540
3541 for (;$y_tmp <= $y_end; $y_tmp += $delta_y) {
3542 $ylab = $this->FormatLabel('y', $y_tmp);
3543 $y_pixels = $this->ytr($y_tmp);
3544
3545 // Horizontal grid line
3546 if ($this->draw_y_grid) {
3547 ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1, $y_pixels, $style);
3548 }
3549
3550 // Draw tick mark(s)
3551 $this->DrawYTick($ylab, $y_pixels);
3552 }
3553 return TRUE;
3554 } // function DrawYTicks
3555
3559 function DrawXTick($which_xlab, $which_xpix)
3560 {
3561 // Ticks on X axis
3562 if ($this->x_tick_pos == 'xaxis') {
3563 ImageLine($this->img, $which_xpix, $this->x_axis_y_pixels - $this->x_tick_cross,
3564 $which_xpix, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color);
3565 }
3566
3567 // Ticks on top of the Plot Area
3568 if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') {
3569 ImageLine($this->img, $which_xpix, $this->plot_area[1] - $this->x_tick_length,
3570 $which_xpix, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color);
3571 }
3572
3573 // Ticks on bottom of Plot Area
3574 if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') {
3575 ImageLine($this->img, $which_xpix, $this->plot_area[3] + $this->x_tick_length,
3576 $which_xpix, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color);
3577 }
3578
3579 // Label on X axis
3580 if ($this->x_tick_label_pos == 'xaxis') {
3581 $this->DrawText($this->x_label_font, $this->x_label_angle,
3582 $which_xpix, $this->x_axis_y_pixels + $this->x_label_axis_offset,
3583 $this->ndx_text_color, $which_xlab, 'center', 'top');
3584 }
3585
3586 // Label on top of the Plot Area
3587 if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') {
3588 $this->DrawText($this->x_label_font, $this->x_label_angle,
3589 $which_xpix, $this->plot_area[1] - $this->x_label_top_offset,
3590 $this->ndx_text_color, $which_xlab, 'center', 'bottom');
3591 }
3592
3593 // Label on bottom of the Plot Area
3594 if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') {
3595 $this->DrawText($this->x_label_font, $this->x_label_angle,
3596 $which_xpix, $this->plot_area[3] + $this->x_label_bot_offset,
3597 $this->ndx_text_color, $which_xlab, 'center', 'top');
3598 }
3599 return TRUE;
3600 }
3601
3609 function DrawXTicks()
3610 {
3611 // Sets the line style for IMG_COLOR_STYLED lines (grid)
3612 if ($this->dashed_grid) {
3613 $this->SetDashedStyle($this->ndx_light_grid_color);
3614 $style = IMG_COLOR_STYLED;
3615 } else {
3616 $style = $this->ndx_light_grid_color;
3617 }
3618
3619 // Calculate the tick start, end, and step:
3620 list($x_tmp, $x_end, $delta_x) = $this->CalcTicks('x');
3621
3622 for (;$x_tmp <= $x_end; $x_tmp += $delta_x) {
3623 $xlab = $this->FormatLabel('x', $x_tmp);
3624 $x_pixels = $this->xtr($x_tmp);
3625
3626 // Vertical grid lines
3627 if ($this->draw_x_grid) {
3628 ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style);
3629 }
3630
3631 // Draw tick mark(s)
3632 $this->DrawXTick($xlab, $x_pixels);
3633 }
3634 return TRUE;
3635 } // function DrawXTicks
3636
3637
3642 {
3643 switch ($this->plot_border_type) {
3644 case 'left': // for past compatibility
3645 case 'plotleft':
3646 ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
3647 $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
3648 break;
3649 case 'right':
3650 case 'plotright':
3651 ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
3652 $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
3653 break;
3654 case 'both':
3655 case 'sides':
3656 ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
3657 $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
3658 ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
3659 $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
3660 break;
3661 case 'none':
3662 //Draw No Border
3663 break;
3664 case 'full':
3665 default:
3666 ImageRectangle($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
3667 $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
3668 break;
3669 }
3670 return TRUE;
3671 }
3672
3673
3678 function DrawDataLabel($which_font, $which_angle, $x_world, $y_world, $which_color, $which_text,
3679 $which_halign = 'center', $which_valign = 'bottom', $x_adjustment=0, $y_adjustment=0)
3680 {
3681 $data_label = $this->FormatLabel('y', $which_text);
3682 //since DrawDataLabel is going to be called alot - perhaps for speed it is better to
3683 //not use this if statement and just always assume which_font is x_label_font (ditto for color).
3684 if ( empty($which_font) )
3685 $which_font = $this->x_label_font;
3686
3687 $which_angle = empty($which_angle)?'0':$this->x_label_angle;
3688
3689 if ( empty($which_color) )
3690 $which_color = $this->ndx_title_color;
3691
3692 $x_pixels = $this->xtr($x_world) + $x_adjustment;
3693 $y_pixels = $this->ytr($y_world) + $y_adjustment;
3694
3695 $this->DrawText($which_font, $which_angle, $x_pixels, $y_pixels,
3696 $which_color, $data_label, $which_halign, $which_valign);
3697
3698 return TRUE;
3699
3700 }
3709 function DrawXDataLabel($xlab, $xpos, $row=FALSE)
3710 {
3711 // FIXME!! not working...
3712 // if (($this->_x_label_cnt++ % $this->x_label_inc) != 0)
3713 // return;
3714
3715 $xlab = $this->FormatLabel('x', $xlab);
3716
3717 // Labels below the plot area
3718 if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
3719 $this->DrawText($this->x_label_font, $this->x_label_angle,
3720 $xpos, $this->plot_area[3] + $this->x_label_bot_offset,
3721 $this->ndx_text_color, $xlab, 'center', 'top');
3722
3723 // Labels above the plot area
3724 if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
3725 $this->DrawText($this->x_label_font, $this->x_label_angle,
3726 $xpos, $this->plot_area[1] - $this->x_label_top_offset,
3727 $this->ndx_text_color, $xlab, 'center', 'bottom');
3728
3729 // $row=0 means this is the first row. $row=FALSE means don't do any rows.
3730 if ($row !== FALSE && $this->draw_x_data_label_lines)
3731 $this->DrawXDataLine($xpos, $row);
3732 return TRUE;
3733 }
3734
3743 function DrawXDataLine($xpos, $row)
3744 {
3745 // Sets the line style for IMG_COLOR_STYLED lines (grid)
3746 if($this->dashed_grid) {
3747 $this->SetDashedStyle($this->ndx_light_grid_color);
3748 $style = IMG_COLOR_STYLED;
3749 } else {
3750 $style = $this->ndx_light_grid_color;
3751 }
3752
3753 // Lines from the bottom up
3754 if ($this->x_data_label_pos == 'both') {
3755 ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style);
3756 }
3757 // Lines from the bottom of the plot up to the max Y value at this X:
3758 else if ($this->x_data_label_pos == 'plotdown') {
3759 $ypos = $this->ytr($this->data_maxy[$row]);
3760 ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style);
3761 }
3762 // Lines from the top of the plot down to the min Y value at this X:
3763 else if ($this->x_data_label_pos == 'plotup') {
3764 $ypos = $this->ytr($this->data_miny[$row]);
3765 ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style);
3766 }
3767 return TRUE;
3768 }
3769
3770
3776 function DrawLegend()
3777 {
3778 // Find maximum legend label line width.
3779 $max_width = 0;
3780 foreach ($this->legend as $line) {
3781 list($width, $unused) = $this->SizeText($this->legend_font, 0, $line);
3782 if ($width > $max_width) $max_width = $width;
3783 }
3784
3785 // For the color box and line spacing, use a typical font character: 8.
3786 list($char_w, $char_h) = $this->SizeText($this->legend_font, 0, '8');
3787
3788 // Normalize text alignment and colorbox alignment variables:
3789 $text_align = isset($this->legend_text_align) ? $this->legend_text_align : 'right';
3790 $colorbox_align = isset($this->legend_colorbox_align) ? $this->legend_colorbox_align : 'right';
3791
3792 // Sizing parameters:
3793 $v_margin = $char_h/2; // Between vertical borders and labels
3794 $dot_height = $char_h + $this->line_spacing; // Height of the small colored boxes
3795 // Overall legend box width e.g.: | space colorbox space text space |
3796 // where colorbox and each space are 1 char width.
3797 if ($colorbox_align != 'none') {
3798 $width = $max_width + 4 * $char_w;
3799 $draw_colorbox = True;
3800 } else {
3801 $width = $max_width + 2 * $char_w;
3802 $draw_colorbox = False;
3803 }
3804
3806 // User-defined position specified?
3807 if ( !isset($this->legend_x_pos) || !isset($this->legend_y_pos)) {
3808 // No, use default
3809 $box_start_x = $this->plot_area[2] - $width - $this->safe_margin;
3810 $box_start_y = $this->plot_area[1] + $this->safe_margin;
3811 } elseif (isset($this->legend_xy_world)) {
3812 // User-defined position in world-coordinates (See SetLegendWorld).
3813 $box_start_x = $this->xtr($this->legend_x_pos);
3814 $box_start_y = $this->ytr($this->legend_y_pos);
3815 unset($this->legend_xy_world);
3816 } else {
3817 // User-defined position in pixel coordinates.
3818 $box_start_x = $this->legend_x_pos;
3819 $box_start_y = $this->legend_y_pos;
3820 }
3821
3822 // Lower right corner
3823 $box_end_y = $box_start_y + $dot_height*(count($this->legend)) + 2*$v_margin;
3824 $box_end_x = $box_start_x + $width;
3825
3826 // Draw outer box
3827 ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_bg_color);
3828 ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_grid_color);
3829
3830 $color_index = 0;
3831 $max_color_index = count($this->ndx_data_colors) - 1;
3832
3833 // Calculate color box and text horizontal positions.
3834 if (!$draw_colorbox) {
3835 if ($text_align == 'left')
3836 $x_pos = $box_start_x + $char_w;
3837 else
3838 $x_pos = $box_end_x - $char_w;
3839 } elseif ($colorbox_align == 'left') {
3840 $dot_left_x = $box_start_x + $char_w;
3841 $dot_right_x = $dot_left_x + $char_w;
3842 if ($text_align == 'left')
3843 $x_pos = $dot_left_x + 2 * $char_w;
3844 else
3845 $x_pos = $box_end_x - $char_w;
3846 } else {
3847 $dot_left_x = $box_end_x - 2 * $char_w;
3848 $dot_right_x = $dot_left_x + $char_w;
3849 if ($text_align == 'left')
3850 $x_pos = $box_start_x + $char_w;
3851 else
3852 $x_pos = $dot_left_x - $char_w;
3853 }
3854
3855 // Calculate starting position of first text line. The bottom of each color box
3856 // lines up with the bottom (baseline) of its text line.
3857 $y_pos = $box_start_y + $v_margin + $dot_height;
3858
3859 foreach ($this->legend as $leg) {
3860 // Draw text with requested alignment:
3861 $this->DrawText($this->legend_font, 0, $x_pos, $y_pos,
3862 $this->ndx_text_color, $leg, $text_align, 'bottom');
3863 if ($draw_colorbox) {
3864 // Draw a box in the data color
3865 $y1 = $y_pos - $dot_height + 1;
3866 $y2 = $y_pos - 1;
3867 ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2,
3868 $this->ndx_data_colors[$color_index]);
3869 // Draw a rectangle around the box
3870 ImageRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2,
3871 $this->ndx_text_color);
3872 }
3873 $y_pos += $dot_height;
3874
3875 $color_index++;
3876 if ($color_index > $max_color_index)
3877 $color_index = 0;
3878 }
3879 return TRUE;
3880 } // Function DrawLegend()
3881
3882
3887 {
3888 // Calculate available room
3889 // Calculate length of all items (boxes included)
3890 // Calculate number of lines and room it would take. FIXME: this should be known in CalcMargins()
3891 // Draw.
3892 }
3893
3897
3898
3909 function DrawPieChart()
3910 {
3911 $xpos = $this->plot_area[0] + $this->plot_area_width/2;
3912 $ypos = $this->plot_area[1] + $this->plot_area_height/2;
3913 $diameter = min($this->plot_area_width, $this->plot_area_height);
3914 $radius = $diameter/2;
3915
3916 // Get sum of each column? One pie slice per column
3917 if ($this->data_type === 'text-data') {
3918 for ($i = 0; $i < $this->num_data_rows; $i++) {
3919 for ($j = 1; $j < $this->num_recs[$i]; $j++) { // Label ($row[0]) unused in these pie charts
3920 @ $sumarr[$j] += abs($this->data[$i][$j]); // NOTE! sum > 0 to make pie charts
3921 }
3922 }
3923 }
3924 // Or only one column per row, one pie slice per row?
3925 else if ($this->data_type == 'text-data-single') {
3926 for ($i = 0; $i < $this->num_data_rows; $i++) {
3927 $legend[$i] = $this->data[$i][0]; // Set the legend to column labels
3928 $sumarr[$i] = $this->data[$i][1];
3929 }
3930 }
3931 else if ($this->data_type == 'data-data') {
3932 for ($i = 0; $i < $this->num_data_rows; $i++) {
3933 for ($j = 2; $j < $this->num_recs[$i]; $j++) {
3934 @ $sumarr[$j] += abs($this->data[$i][$j]);
3935 }
3936 }
3937 }
3938 else {
3939 return $this->PrintError("DrawPieChart(): Data type '$this->data_type' not supported.");
3940 }
3941
3942 $total = array_sum($sumarr);
3943
3944 if ($total == 0) {
3945 return $this->PrintError('DrawPieChart(): Empty data set');
3946 }
3947
3948 if ($this->shading) {
3949 $diam2 = $diameter / 2;
3950 } else {
3951 $diam2 = $diameter;
3952 }
3953 $max_data_colors = count ($this->data_colors);
3954
3955 for ($h = $this->shading; $h >= 0; $h--) {
3956 $color_index = 0;
3957 $start_angle = 0;
3958 $end_angle = 0;
3959 foreach ($sumarr as $val) {
3960 // For shaded pies: the last one (at the top of the "stack") has a brighter color:
3961 if ($h == 0)
3962 $slicecol = $this->ndx_data_colors[$color_index];
3963 else
3964 $slicecol = $this->ndx_data_dark_colors[$color_index];
3965
3966 $label_txt = $this->number_format(($val / $total * 100), $this->y_precision) . '%';
3967 $val = 360 * ($val / $total);
3968
3969 // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why),
3970 // so the pie chart would start clockwise from 3 o'clock, would it not be
3971 // for the reversal of start and end angles in imagefilledarc()
3972 // Also note ImageFilledArc only takes angles in integer degrees, and if the
3973 // the start and end angles match then you get a full circle not a zero-width
3974 // pie. This is bad. So skip any zero-size wedge. On the other hand, we cannot
3975 // let cumulative error from rounding to integer result in missing wedges. So
3976 // keep the running total as a float, and round the angles. It should not
3977 // be necessary to check that the last wedge ends at 360 degrees.
3978 $start_angle = $end_angle;
3979 $end_angle += $val;
3980 // This method of conversion to integer - truncate after reversing it - was
3981 // chosen to match the implicit method of PHPlot<=5.0.4 to get the same slices.
3982 $arc_start_angle = (int)(360 - $start_angle);
3983 $arc_end_angle = (int)(360 - $end_angle);
3984
3985 if ($arc_start_angle > $arc_end_angle) {
3986 $mid_angle = deg2rad($end_angle - ($val / 2));
3987
3988 // Draw the slice
3989 ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
3990 $arc_end_angle, $arc_start_angle,
3991 $slicecol, IMG_ARC_PIE);
3992
3993 // Draw the labels only once
3994 if ($h == 0) {
3995 // Draw the outline
3996 if (! $this->shading)
3997 ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2,
3998 $arc_end_angle, $arc_start_angle,
3999 $this->ndx_grid_color, IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL);
4000
4001
4002 // The '* 1.2' trick is to get labels out of the pie chart so there are more
4003 // chances they can be seen in small sectors.
4004 $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position;
4005 $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position;
4006
4007 $this->DrawText($this->generic_font, 0, $label_x, $label_y, $this->ndx_grid_color,
4008 $label_txt, 'center', 'center');
4009 }
4010 }
4011 if (++$color_index >= $max_data_colors)
4012 $color_index = 0;
4013 } // end for
4014 } // end for
4015 return TRUE;
4016 }
4017
4018
4023 function DrawDotsError()
4024 {
4025 if ($this->data_type != 'data-data-error') {
4026 return $this->PrintError("DrawDotsError(): Data type '$this->data_type' not supported.");
4027 }
4028
4029 // Suppress duplicate X data labels in linepoints mode; let DrawLinesError() do them.
4030 $do_labels = ($this->plot_type != 'linepoints');
4031
4032 for($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4033 $record = 1; // Skip record #0 (title)
4034
4035 $x_now = $this->data[$row][$record++]; // Read it, advance record index
4036
4037 $x_now_pixels = $this->xtr($x_now); // Absolute coordinates.
4038
4039 // Draw X Data labels?
4040 if ($this->x_data_label_pos != 'none' && $do_labels)
4041 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
4042
4043 // Now go for Y, E+, E-
4044 for ($idx = 0; $record < $this->num_recs[$row]; $idx++) {
4045 // Y:
4046 $y_now = $this->data[$row][$record++];
4047 $this->DrawDot($x_now, $y_now, $idx, $this->ndx_data_colors[$idx]);
4048
4049 // Error +
4050 $val = $this->data[$row][$record++];
4051 $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
4052 $this->ndx_error_bar_colors[$idx]);
4053 // Error -
4054 $val = $this->data[$row][$record++];
4055 $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
4056 $this->ndx_error_bar_colors[$idx]);
4057 }
4058 }
4059 return TRUE;
4060 } // function DrawDotsError()
4061
4062
4063 /*
4064 * Supported data types:
4065 * - data-data ("title", x, y1, y2, y3, ...)
4066 * - text-data ("title", y1, y2, y3, ...)
4067 */
4068 function DrawDots()
4069 {
4070 if (!$this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__))
4071 return FALSE;
4072
4073 // Suppress duplicate X data labels in linepoints mode; let DrawLines() do them.
4074 $do_labels = ($this->plot_type != 'linepoints');
4075
4076 for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4077 $rec = 1; // Skip record #0 (data label)
4078
4079 // Do we have a value for X?
4080 if ($this->data_type == 'data-data')
4081 $x_now = $this->data[$row][$rec++]; // Read it, advance record index
4082 else
4083 $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc...
4084
4085 $x_now_pixels = $this->xtr($x_now);
4086
4087 // Draw X Data labels?
4088 if ($this->x_data_label_pos != 'none' && $do_labels)
4089 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
4090
4091 // Proceed with Y values
4092 for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
4093 if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data
4094 $this->DrawDot($x_now, $this->data[$row][$rec],
4095 $idx, $this->ndx_data_colors[$idx]);
4096 }
4097 }
4098 }
4099 return TRUE;
4100 } //function DrawDots
4101
4102
4107 {
4108 if (!$this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__))
4109 return FALSE;
4110
4111 for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4112 $rec = 1; // Skip record #0 (data label)
4113
4114 // Do we have a value for X?
4115 if ($this->data_type == 'data-data')
4116 $x_now = $this->data[$row][$rec++]; // Read it, advance record index
4117 else
4118 $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc...
4119
4120 $x_now_pixels = $this->xtr($x_now);
4121
4122 // Draw X Data labels?
4123 if ($this->x_data_label_pos != 'none')
4124 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
4125
4126 // Proceed with Y values
4127 for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) {
4128 if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data
4129 ImageSetThickness($this->img, $this->line_widths[$idx]);
4130 // Draws a line from user defined x axis position up to ytr($val)
4131 ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, $x_now_pixels,
4132 $this->ytr($this->data[$row][$rec]), $this->ndx_data_colors[$idx]);
4133 }
4134 }
4135 }
4136
4137 ImageSetThickness($this->img, 1);
4138 return TRUE;
4139 } //function DrawThinBarLines
4140
4144 function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color)
4145 {
4146 /*
4147 // TODO: add a parameter to show datalabels next to error bars?
4148 // something like this:
4149 if ($this->x_data_label_pos == 'plot')
4150 $this->DrawText($this->error_font, 90, $x1, $y2,
4151 $color, $label, 'center', 'bottom');
4152 */
4153
4154 $x1 = $this->xtr($x_world);
4155 $y1 = $this->ytr($y_world);
4156 $y2 = $this->ytr($y_world+$error_height) ;
4157
4158 ImageSetThickness($this->img, $this->error_bar_line_width);
4159 ImageLine($this->img, $x1, $y1 , $x1, $y2, $color);
4160
4161 switch ($error_bar_type) {
4162 case 'line':
4163 break;
4164 case 'tee':
4165 ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color);
4166 break;
4167 default:
4168 ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color);
4169 break;
4170 }
4171
4172 ImageSetThickness($this->img, 1);
4173 return TRUE;
4174 }
4175
4181 function DrawDot($x_world, $y_world, $record, $color)
4182 {
4183 // TODO: optimize, avoid counting every time we are called.
4184 $record = $record % count ($this->point_shapes);
4185
4186 $half_point = $this->point_sizes[$record] / 2;
4187
4188 $x_mid = $this->xtr($x_world);
4189 $y_mid = $this->ytr($y_world);
4190
4191 $x1 = $x_mid - $half_point;
4192 $x2 = $x_mid + $half_point;
4193 $y1 = $y_mid - $half_point;
4194 $y2 = $y_mid + $half_point;
4195
4196 switch ($this->point_shapes[$record]) {
4197 case 'halfline':
4198 ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color);
4199 break;
4200 case 'line':
4201 ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
4202 break;
4203 case 'plus':
4204 ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
4205 ImageLine($this->img, $x_mid, $y1, $x_mid, $y2, $color);
4206 break;
4207 case 'cross':
4208 ImageLine($this->img, $x1, $y1, $x2, $y2, $color);
4209 ImageLine($this->img, $x1, $y2, $x2, $y1, $color);
4210 break;
4211 case 'rect':
4212 ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
4213 break;
4214 case 'circle':
4215 ImageArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record],
4216 0, 360, $color);
4217 break;
4218 case 'dot':
4219 ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record],
4220 $this->point_sizes[$record], 0, 360, $color, IMG_ARC_PIE);
4221 break;
4222 case 'diamond':
4223 $arrpoints = array( $x1, $y_mid, $x_mid, $y1, $x2, $y_mid, $x_mid, $y2);
4224 ImageFilledPolygon($this->img, $arrpoints, 4, $color);
4225 break;
4226 case 'triangle':
4227 $arrpoints = array( $x1, $y_mid, $x2, $y_mid, $x_mid, $y2);
4228 ImageFilledPolygon($this->img, $arrpoints, 3, $color);
4229 break;
4230 case 'trianglemid':
4231 $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y_mid);
4232 ImageFilledPolygon($this->img, $arrpoints, 3, $color);
4233 break;
4234 case 'none':
4235 break;
4236 default:
4237 ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
4238 break;
4239 }
4240 return TRUE;
4241 }
4242
4254 function DrawArea()
4255 {
4256 $incomplete_data_defaults_to_x_axis = FALSE; // TODO: make this configurable
4257
4258 for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4259 $rec = 1; // Skip record #0 (data label)
4260
4261 if ($this->data_type == 'data-data') // Do we have a value for X?
4262 $x_now = $this->data[$row][$rec++]; // Read it, advance record index
4263 else
4264 $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc...
4265
4266 $x_now_pixels = $this->xtr($x_now); // Absolute coordinates
4267
4268
4269 if ($this->x_data_label_pos != 'none') // Draw X Data labels?
4270 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
4271
4272 // Proceed with Y values
4273 // Create array of points for imagefilledpolygon()
4274 for($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) {
4275 if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data
4276 $y_now_pixels = $this->ytr($this->data[$row][$rec]);
4277
4278 $posarr[$idx][] = $x_now_pixels;
4279 $posarr[$idx][] = $y_now_pixels;
4280
4281 $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1;
4282 }
4283 // If there's missing data...
4284 else {
4285 if (isset ($incomplete_data_defaults_to_x_axis)) {
4286 $posarr[$idx][] = $x_now_pixels;
4287 $posarr[$idx][] = $this->x_axis_y_pixels;
4288 $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx]+1 : 1;
4289 }
4290 }
4291 }
4292 } // end for
4293
4294 $end = count($posarr);
4295 for ($i = 0; $i < $end; $i++) {
4296 // Prepend initial points. X = first point's X, Y = x_axis_y_pixels
4297 $x = $posarr[$i][0];
4298 array_unshift($posarr[$i], $x, $this->x_axis_y_pixels);
4299
4300 // Append final points. X = last point's X, Y = x_axis_y_pixels
4301 $x = $posarr[$i][count($posarr[$i])-2];
4302 array_push($posarr[$i], $x, $this->x_axis_y_pixels);
4303
4304 $num_points[$i] += 2;
4305
4306 // Draw the poligon
4307 ImageFilledPolygon($this->img, $posarr[$i], $num_points[$i], $this->ndx_data_colors[$i]);
4308 }
4309 return TRUE;
4310 } // function DrawArea()
4311
4312
4319 function DrawLines()
4320 {
4321 // This will tell us if lines have already begun to be drawn.
4322 // It is an array to keep separate information for every line, with a single
4323 // variable we would sometimes get "undefined offset" errors and no plot...
4324 $start_lines = array_fill(0, $this->records_per_group, FALSE);
4325
4326 if ($this->data_type == 'text-data') {
4327 $lastx[0] = $this->xtr(0);
4328 $lasty[0] = $this->xtr(0);
4329 }
4330
4331 for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4332 $record = 1; // Skip record #0 (data label)
4333
4334 if ($this->data_type == 'data-data') // Do we have a value for X?
4335 $x_now = $this->data[$row][$record++]; // Read it, advance record index
4336 else
4337 $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc...
4338
4339 $x_now_pixels = $this->xtr($x_now); // Absolute coordinates
4340
4341 if ($this->x_data_label_pos != 'none') // Draw X Data labels?
4342 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
4343
4344 for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
4345 if (($line_style = $this->line_styles[$idx]) == 'none')
4346 continue; //Allow suppressing entire line, useful with linepoints
4347 if (is_numeric($this->data[$row][$record])) { //Allow for missing Y data
4348 $y_now_pixels = $this->ytr($this->data[$row][$record]);
4349
4350 if ($start_lines[$idx] == TRUE) {
4351 // Set line width, revert it to normal at the end
4352 ImageSetThickness($this->img, $this->line_widths[$idx]);
4353
4354 if ($line_style == 'dashed') {
4355 $this->SetDashedStyle($this->ndx_data_colors[$idx]);
4356 ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
4357 IMG_COLOR_STYLED);
4358 } else {
4359 ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
4360 $this->ndx_data_colors[$idx]);
4361 }
4362
4363 }
4364 $lasty[$idx] = $y_now_pixels;
4365 $lastx[$idx] = $x_now_pixels;
4366 $start_lines[$idx] = TRUE;
4367 }
4368 // Y data missing... should we leave a blank or not?
4369 else if ($this->draw_broken_lines) {
4370 $start_lines[$idx] = FALSE;
4371 }
4372 } // end for
4373 } // end for
4374
4375 ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later.
4376 return TRUE;
4377 } // function DrawLines()
4378
4379
4385 {
4386 if ($this->data_type != 'data-data-error') {
4387 return $this->PrintError("DrawLinesError(): Data type '$this->data_type' not supported.");
4388 }
4389
4390 $start_lines = array_fill(0, $this->records_per_group, FALSE);
4391
4392 for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4393 $record = 1; // Skip record #0 (data label)
4394
4395 $x_now = $this->data[$row][$record++]; // Read X value, advance record index
4396
4397 $x_now_pixels = $this->xtr($x_now); // Absolute coordinates.
4398
4399
4400 if ($this->x_data_label_pos != 'none') // Draw X Data labels?
4401 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
4402
4403 // Now go for Y, E+, E-
4404 for ($idx = 0; $record < $this->num_recs[$row]; $idx++) {
4405 if (($line_style = $this->line_styles[$idx]) == 'none')
4406 continue; //Allow suppressing entire line, useful with linepoints
4407 // Y
4408 $y_now = $this->data[$row][$record++];
4409 $y_now_pixels = $this->ytr($y_now);
4410
4411 if ($start_lines[$idx] == TRUE) {
4412 ImageSetThickness($this->img, $this->line_widths[$idx]);
4413
4414 if ($line_style == 'dashed') {
4415 $this->SetDashedStyle($this->ndx_data_colors[$idx]);
4416 ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
4417 IMG_COLOR_STYLED);
4418 } else {
4419 ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
4420 $this->ndx_data_colors[$idx]);
4421 }
4422 }
4423
4424 // Error+
4425 $val = $this->data[$row][$record++];
4426 $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
4427 $this->ndx_error_bar_colors[$idx]);
4428
4429 // Error-
4430 $val = $this->data[$row][$record++];
4431 $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
4432 $this->ndx_error_bar_colors[$idx]);
4433
4434 // Update indexes:
4435 $start_lines[$idx] = TRUE; // Tells us if we already drew the first column of points,
4436 // thus having $lastx and $lasty ready for the next column.
4437 $lastx[$idx] = $x_now_pixels;
4438 $lasty[$idx] = $y_now_pixels;
4439 } // end while
4440 } // end for
4441
4442 ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later.
4443 return TRUE;
4444 } // function DrawLinesError()
4445
4446
4447
4451 function DrawSquared()
4452 {
4453 // This will tell us if lines have already begun to be drawn.
4454 // It is an array to keep separate information for every line, for with a single
4455 // variable we could sometimes get "undefined offset" errors and no plot...
4456 $start_lines = array_fill(0, $this->records_per_group, FALSE);
4457
4458 if ($this->data_type == 'text-data') {
4459 $lastx[0] = $this->xtr(0);
4460 $lasty[0] = $this->xtr(0);
4461 }
4462
4463 for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) {
4464 $record = 1; // Skip record #0 (data label)
4465
4466 if ($this->data_type == 'data-data') // Do we have a value for X?
4467 $x_now = $this->data[$row][$record++]; // Read it, advance record index
4468 else
4469 $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc...
4470
4471 $x_now_pixels = $this->xtr($x_now); // Absolute coordinates
4472
4473 if ($this->x_data_label_pos != 'none') // Draw X Data labels?
4474 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param.
4475
4476 // Draw Lines
4477 for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
4478 if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data
4479 $y_now_pixels = $this->ytr($this->data[$row][$record]);
4480
4481 if ($start_lines[$idx] == TRUE) {
4482 // Set line width, revert it to normal at the end
4483 ImageSetThickness($this->img, $this->line_widths[$idx]);
4484
4485 if ($this->line_styles[$idx] == 'dashed') {
4486 $this->SetDashedStyle($this->ndx_data_colors[$idx]);
4487 ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
4488 IMG_COLOR_STYLED);
4489 ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels,
4490 IMG_COLOR_STYLED);
4491 } else {
4492 ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
4493 $this->ndx_data_colors[$idx]);
4494 ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels,
4495 $this->ndx_data_colors[$idx]);
4496 }
4497 }
4498 $lastx[$idx] = $x_now_pixels;
4499 $lasty[$idx] = $y_now_pixels;
4500 $start_lines[$idx] = TRUE;
4501 }
4502 // Y data missing... should we leave a blank or not?
4503 else if ($this->draw_broken_lines) {
4504 $start_lines[$idx] = FALSE;
4505 }
4506 }
4507 } // end while
4508
4509 ImageSetThickness($this->img, 1);
4510 return TRUE;
4511 } // function DrawSquared()
4512
4513
4517 function DrawBars()
4518 {
4519 if ($this->data_type != 'text-data') {
4520 return $this->PrintError('DrawBars(): Bar plots must be text-data: use function SetDataType("text-data")');
4521 }
4522
4523 // This is the X offset from the bar group's label center point to the left side of the first bar
4524 // in the group. See also CalcBarWidths above.
4525 $x_first_bar = (($this->records_per_group - 1) * $this->record_bar_width) / 2 - $this->bar_adjust_gap;
4526
4527 for ($row = 0; $row < $this->num_data_rows; $row++) {
4528 $record = 1; // Skip record #0 (data label)
4529
4530 $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc...
4531
4532 if ($this->x_data_label_pos != 'none') // Draw X Data labels?
4533 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
4534
4535 // Lower left X of first bar in the group:
4536 $x1 = $x_now_pixels - $x_first_bar;
4537
4538 // Draw the bars in the group:
4539 for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
4540 if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data
4541 $x2 = $x1 + $this->actual_bar_width;
4542
4543 if ($this->data[$row][$record] < $this->x_axis_position) {
4544 $y1 = $this->x_axis_y_pixels;
4545 $y2 = $this->ytr($this->data[$row][$record]);
4546 $upgoing_bar = False;
4547 } else {
4548 $y1 = $this->ytr($this->data[$row][$record]);
4549 $y2 = $this->x_axis_y_pixels;
4550 $upgoing_bar = True;
4551 }
4552
4553 // Draw the bar
4554 ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
4555
4556 if ($this->shading) { // Draw the shade?
4557 ImageFilledPolygon($this->img, array($x1, $y1,
4558 $x1 + $this->shading, $y1 - $this->shading,
4559 $x2 + $this->shading, $y1 - $this->shading,
4560 $x2 + $this->shading, $y2 - $this->shading,
4561 $x2, $y2,
4562 $x2, $y1),
4563 6, $this->ndx_data_dark_colors[$idx]);
4564 }
4565 // Or draw a border?
4566 else {
4567 ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]);
4568 }
4569
4570 // Draw optional data labels above the bars (or below, for negative values).
4571 if ( $this->y_data_label_pos == 'plotin') {
4572 if ($upgoing_bar) {
4573 $v_align = 'bottom';
4574 $y_offset = -5 - $this->shading;
4575 } else {
4576 $v_align = 'top';
4577 $y_offset = 2;
4578 }
4579 $this->DrawDataLabel($this->y_label_font, NULL, $row+0.5, $this->data[$row][$record], '',
4580 $this->data[$row][$record], 'center', $v_align,
4581 ($idx + 0.5) * $this->record_bar_width - $x_first_bar, $y_offset);
4582 }
4583
4584 }
4585 // Step to next bar in group:
4586 $x1 += $this->record_bar_width;
4587 } // end for
4588 } // end for
4589 return TRUE;
4590 } //function DrawBars
4591
4592
4598 {
4599 if ($this->data_type != 'text-data') {
4600 return $this->PrintError('DrawStackedBars(): Bar plots must be text-data: use SetDataType("text-data")');
4601 }
4602
4603 // This is the X offset from the bar's label center point to the left side of the bar.
4604 $x_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap;
4605
4606 for ($row = 0; $row < $this->num_data_rows; $row++) {
4607 $record = 1; // Skip record #0 (data label)
4608
4609 $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc...
4610
4611 if ($this->x_data_label_pos != 'none') // Draw X Data labels?
4612 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
4613
4614 // Lower left and lower right X of the bars in this group:
4615 $x1 = $x_now_pixels - $x_first_bar;
4616 $x2 = $x1 + $this->actual_bar_width;
4617
4618 // Draw the bars
4619 $oldv = 0;
4620 for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) {
4621 if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data
4622
4623 $y1 = $this->ytr(abs($this->data[$row][$record]) + $oldv);
4624 $y2 = $this->ytr($this->x_axis_position + $oldv);
4625 $oldv += abs($this->data[$row][$record]);
4626
4627 // Draw the bar
4628 ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
4629
4630 if ($this->shading) { // Draw the shade?
4631 ImageFilledPolygon($this->img, array($x1, $y1,
4632 $x1 + $this->shading, $y1 - $this->shading,
4633 $x2 + $this->shading, $y1 - $this->shading,
4634 $x2 + $this->shading, $y2 - $this->shading,
4635 $x2, $y2,
4636 $x2, $y1),
4637 6, $this->ndx_data_dark_colors[$idx]);
4638 }
4639 // Or draw a border?
4640 else {
4641 ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]);
4642 }
4643 }
4644 } // end for
4645 } // end for
4646 return TRUE;
4647 } //function DrawStackedBars
4648
4649
4653 function DrawGraph()
4654 {
4655 // Test for missing image, missing data, empty data:
4656 if (! $this->img) {
4657 return $this->PrintError('DrawGraph(): No image resource allocated');
4658 }
4659 if (empty($this->data) || ! is_array($this->data)) {
4660 return $this->PrintError("DrawGraph(): No data array");
4661 }
4662 if ($this->total_records == 0) {
4663 return $this->PrintError('DrawGraph(): Empty data set');
4664 }
4665
4666 // For pie charts: don't draw grid or border or axes, and maximize area usage.
4667 // These controls can be split up in the future if needed.
4668 $draw_axes = ($this->plot_type != 'pie');
4669
4670 // Get maxima and minima for scaling:
4671 if (!$this->FindDataLimits())
4672 return FALSE;
4673
4674 // Set plot area world values (plot_max_x, etc.):
4675 if (!$this->CalcPlotAreaWorld())
4676 return FALSE;
4677
4678 // Calculate X and Y axis positions in World Coordinates:
4679 $this->CalcAxisPositions();
4680
4681 // Calculate the plot margins, if needed.
4682 // For pie charts, set the $maximize argument to maximize space usage.
4683 $this->CalcMargins(!$draw_axes);
4684
4685 // Calculate the actual plot area in device coordinates:
4686 $this->CalcPlotAreaPixels();
4687
4688 // Calculate the mapping between world and device coordinates:
4689 $this->CalcTranslation();
4690
4691 // Pad color and style arrays to fit records per group:
4692 $this->PadArrays();
4693 $this->DoCallback('draw_setup');
4694
4695 $this->DrawBackground();
4696 $this->DrawImageBorder();
4697 $this->DoCallback('draw_image_background');
4698
4699 $this->DrawPlotAreaBackground();
4700 $this->DoCallback('draw_plotarea_background');
4701
4702 $this->DrawTitle();
4703 $this->DrawXTitle();
4704 $this->DrawYTitle();
4705 $this->DoCallback('draw_titles');
4706
4707 if ($draw_axes && ! $this->grid_at_foreground) { // Usually one wants grids to go back, but...
4708 $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis())
4709 $this->DrawXAxis();
4710 $this->DoCallback('draw_axes');
4711 }
4712
4713 switch ($this->plot_type) {
4714 case 'thinbarline':
4715 $this->DrawThinBarLines();
4716 break;
4717 case 'area':
4718 $this->DrawArea();
4719 break;
4720 case 'squared':
4721 $this->DrawSquared();
4722 break;
4723 case 'lines':
4724 if ( $this->data_type == 'data-data-error') {
4725 $this->DrawLinesError();
4726 } else {
4727 $this->DrawLines();
4728 }
4729 break;
4730 case 'linepoints':
4731 if ( $this->data_type == 'data-data-error') {
4732 $this->DrawLinesError();
4733 $this->DrawDotsError();
4734 } else {
4735 $this->DrawLines();
4736 $this->DrawDots();
4737 }
4738 break;
4739 case 'points';
4740 if ( $this->data_type == 'data-data-error') {
4741 $this->DrawDotsError();
4742 } else {
4743 $this->DrawDots();
4744 }
4745 break;
4746 case 'pie':
4747 $this->DrawPieChart();
4748 break;
4749 case 'stackedbars':
4750 $this->CalcBarWidths();
4751 $this->DrawStackedBars();
4752 break;
4753 case 'bars':
4754 default:
4755 $this->plot_type = 'bars'; // Set it if it wasn't already set. (necessary?)
4756 $this->CalcBarWidths();
4757 $this->DrawBars();
4758 break;
4759 } // end switch
4760 $this->DoCallback('draw_graph');
4761
4762 if ($draw_axes && $this->grid_at_foreground) { // Usually one wants grids to go back, but...
4763 $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis())
4764 $this->DrawXAxis();
4765 $this->DoCallback('draw_axes');
4766 }
4767
4768 if ($draw_axes) {
4769 $this->DrawPlotBorder();
4770 $this->DoCallback('draw_border');
4771 }
4772
4773 if ($this->legend) {
4774 $this->DrawLegend();
4775 $this->DoCallback('draw_legend');
4776 }
4777
4778 if ($this->print_image && !$this->PrintImage())
4779 return FALSE;
4780
4781 return TRUE;
4782 } //function DrawGraph()
4783
4787
4791 function SetDrawVertTicks($which_dvt)
4792 {
4793 if ($which_dvt != 1)
4794 $this->SetYTickPos('none');
4795 return TRUE;
4796 }
4797
4801 function SetDrawHorizTicks($which_dht)
4802 {
4803 if ($which_dht != 1)
4804 $this->SetXTickPos('none');
4805 return TRUE;
4806 }
4807
4812 {
4813 return $this->SetNumXTicks($n);
4814 }
4815
4820 {
4821 return $this->SetNumYTicks($n);
4822 }
4823
4828 {
4829 return $this->SetXTickIncrement($inc);
4830 }
4831
4832
4837 {
4838 return $this->SetYTickIncrement($inc);
4839 }
4840
4844 function SetVertTickPosition($which_tp)
4845 {
4846 return $this->SetYTickPos($which_tp);
4847 }
4848
4852 function SetHorizTickPosition($which_tp)
4853 {
4854 return $this->SetXTickPos($which_tp);
4855 }
4856
4860 function SetTitleFontSize($which_size)
4861 {
4862 return $this->SetFont('title', $which_size);
4863 }
4864
4868 function SetAxisFontSize($which_size)
4869 {
4870 $this->SetFont('x_label', $which_size);
4871 $this->SetFont('y_label', $which_size);
4872 }
4873
4877 function SetSmallFontSize($which_size)
4878 {
4879 return $this->SetFont('generic', $which_size);
4880 }
4881
4885 function SetXLabelFontSize($which_size)
4886 {
4887 return $this->SetFont('x_title', $which_size);
4888 }
4889
4893 function SetYLabelFontSize($which_size)
4894 {
4895 return $this->SetFont('y_title', $which_size);
4896 }
4897
4901 function SetXLabel($which_xlab)
4902 {
4903 return $this->SetXTitle($which_xlab);
4904 }
4905
4909 function SetYLabel($which_ylab)
4910 {
4911 return $this->SetYTitle($which_ylab);
4912 }
4913
4917 function SetTickLength($which_tl)
4918 {
4919 $this->SetXTickLength($which_tl);
4920 $this->SetYTickLength($which_tl);
4921 return TRUE;
4922 }
4923
4927 function SetYGridLabelType($which_yglt)
4928 {
4929 return $this->SetYLabelType($which_yglt);
4930 }
4931
4935 function SetXGridLabelType($which_xglt)
4936 {
4937 return $this->SetXLabelType($which_xglt);
4938 }
4942 function SetYGridLabelPos($which_yglp)
4943 {
4944 return $this->SetYTickLabelPos($which_yglp);
4945 }
4949 function SetXGridLabelPos($which_xglp)
4950 {
4951 return $this->SetXTickLabelPos($which_xglp);
4952 }
4953
4954
4958 function SetXTitlePos($xpos)
4959 {
4960 $this->x_title_pos = $xpos;
4961 return TRUE;
4962 }
4963
4967 function SetYTitlePos($xpos)
4968 {
4969 $this->y_title_pos = $xpos;
4970 return TRUE;
4971 }
4972
4976 function SetXDataLabelAngle($which_xdla)
4977 {
4978 return $this->SetXLabelAngle($which_xdla);
4979 }
4980
4987 function SetDrawXDataLabels($which_dxdl)
4988 {
4989 if ($which_dxdl == '1' )
4990 $this->SetXDataLabelPos('plotdown');
4991 else
4992 $this->SetXDataLabelPos('none');
4993 }
4994
4998 function SetNewPlotAreaPixels($x1, $y1, $x2, $y2)
4999 {
5000 //Like in GD 0, 0 is upper left set via pixel Coordinates
5001 $this->plot_area = array($x1, $y1, $x2, $y2);
5002 $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
5003 $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
5004 $this->y_top_margin = $this->plot_area[1];
5005
5006 if (isset($this->plot_max_x))
5007 $this->CalcTranslation();
5008
5009 return TRUE;
5010 }
5011
5015 function SetColor($which_color)
5016 {
5017 $this->SetRGBColor($which_color);
5018 return TRUE;
5019 }
5020
5021 /*
5022 * \deprecated Use SetLineWidths().
5023 */
5024 function SetLineWidth($which_lw)
5025 {
5026
5027 $this->SetLineWidths($which_lw);
5028
5029 if (!$this->error_bar_line_width) {
5030 $this->SetErrorBarLineWidth($which_lw);
5031 }
5032 return TRUE;
5033 }
5034
5035 /*
5036 * \deprecated Use SetPointShapes().
5037 */
5038 function SetPointShape($which_pt)
5039 {
5040 $this->SetPointShapes($which_pt);
5041 return TRUE;
5042 }
5043
5044 /*
5045 * \deprecated Use SetPointSizes().
5046 */
5047 function SetPointSize($which_ps)
5048 {
5049 $this->SetPointSizes($which_ps);
5050 return TRUE;
5051 }
5052} // class PHPlot
print $file
$n
Definition: RandomTest.php:80
$size
Definition: RandomTest.php:79
DrawPieChart()
Definition: phplot.php:3909
SetYLabelFontSize($which_size)
Definition: phplot.php:4893
SetYGridLabelType($which_yglt)
Definition: phplot.php:4927
$output_file
Definition: phplot.php:57
SetDrawBrokenLines($bl)
Definition: phplot.php:2068
$data_type
Definition: phplot.php:60
SetXAxisPosition($pos)
Definition: phplot.php:1926
DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color)
Definition: phplot.php:4144
SetSkipLeftTick($skip)
Definition: phplot.php:3194
SetHorizTickPosition($which_tp)
Definition: phplot.php:4852
$is_inline
Definition: phplot.php:33
SetErrorBarColors($which_err=NULL)
Definition: phplot.php:634
RemoveCallback($reason)
Definition: phplot.php:2224
SetHorizTickIncrement($inc)
Definition: phplot.php:4827
$color_array
Definition: phplot.php:151
$y_title_txt
Definition: phplot.php:79
CalcMargins($maximize)
Definition: phplot.php:2419
SetInputFile($which_input_file)
Definition: phplot.php:289
SetYLabel($which_ylab)
Definition: phplot.php:4909
DrawXDataLabel($xlab, $xpos, $row=FALSE)
Definition: phplot.php:3709
$line_spacing
Definition: phplot.php:49
$y_precision
Definition: phplot.php:68
SetLegend($which_leg)
Definition: phplot.php:1717
SetYLabelType($which_ylt)
Definition: phplot.php:1632
$y_tick_label_pos
Definition: phplot.php:93
$x_axis_position
Definition: phplot.php:39
SetSmallFontSize($which_size)
Definition: phplot.php:4877
DrawStackedBars()
Definition: phplot.php:4597
$num_x_ticks
Definition: phplot.php:132
$x_tick_label_pos
Definition: phplot.php:92
SetNumVertTicks($n)
Definition: phplot.php:4819
SetIndexDarkColor($which_color)
Definition: phplot.php:329
SetXTickCrossing($which_xc)
Definition: phplot.php:3221
ytr($y_world)
Definition: phplot.php:2934
$plot_border_type
Definition: phplot.php:177
$grid_at_foreground
Definition: phplot.php:148
SetLineWidth($which_lw)
Definition: phplot.php:5024
SetNumHorizTicks($n)
Definition: phplot.php:4811
SetDrawXDataLabelLines($dxdl)
Definition: phplot.php:1838
ProcessText($draw_it, $font, $angle, $x, $y, $color, $text, $halign, $valign)
Definition: phplot.php:1278
DrawXTicks()
Definition: phplot.php:3609
SetXTitlePos($xpos)
Definition: phplot.php:4958
CalcAxisPositions()
Definition: phplot.php:2832
SetXTickIncrement($which_ti='')
Definition: phplot.php:3116
SetTickColor($which_color)
Definition: phplot.php:413
SetBackgroundColor($which_color)
Definition: phplot.php:383
SetXTickLabelPos($which_xtlp)
Definition: phplot.php:1597
SetXScaleType($which_xst)
Definition: phplot.php:1933
$data_colors
Definition: phplot.php:162
$ttf_path
Definition: phplot.php:47
SetTextColor($which_color)
Definition: phplot.php:435
SetYTickCrossing($which_yc)
Definition: phplot.php:3227
$x_title_txt
Definition: phplot.php:76
$draw_y_grid
Definition: phplot.php:145
SetSkipTopTick($skip)
Definition: phplot.php:3176
$skip_top_tick
Definition: phplot.php:138
SetPointShapes($which_pt)
Definition: phplot.php:1993
SetYGridLabelPos($which_yglp)
Definition: phplot.php:4942
$y_tick_inc
Definition: phplot.php:136
SetLineSpacing($which_spc)
Definition: phplot.php:744
$x_tick_inc
Definition: phplot.php:135
SetDataType($which_dt)
Definition: phplot.php:2081
$safe_margin
Definition: phplot.php:37
SetPointShape($which_pt)
Definition: phplot.php:5038
SetDrawDashedGrid($ddg)
Definition: phplot.php:1828
SetPlotAreaPixels($x1, $y1, $x2, $y2)
Definition: phplot.php:2687
SetLineStyles($which_ls=NULL)
Definition: phplot.php:722
$label_scale_position
Definition: phplot.php:63
xtr($x_world)
Definition: phplot.php:2919
SetTitleFontSize($which_size)
Definition: phplot.php:4860
SetLabelColor($which_color)
Definition: phplot.php:424
SetYAxisPosition($pos)
Definition: phplot.php:1916
SetXLabelType($which_xlt)
Definition: phplot.php:1623
SetYTickPos($which_tp)
Definition: phplot.php:3159
DrawBars()
Definition: phplot.php:4517
GetImage($image_filename, &$width, &$height)
Definition: phplot.php:249
DrawError($error_message, $where_x=NULL, $where_y=NULL)
Definition: phplot.php:1541
$y_axis_position
Definition: phplot.php:40
$data_units_text
Definition: phplot.php:71
$group_frac_width
Definition: phplot.php:64
SetDrawHorizTicks($which_dht)
Definition: phplot.php:4801
$data_border_colors
Definition: phplot.php:164
$x_title_pos
Definition: phplot.php:77
SetXTimeFormat($which_xtf)
Definition: phplot.php:1638
SetXTickLength($which_xln)
Definition: phplot.php:3209
DrawXTick($which_xlab, $which_xpix)
Definition: phplot.php:3559
DrawYTicks()
Definition: phplot.php:3528
$draw_broken_lines
Definition: phplot.php:183
$y_tick_length
Definition: phplot.php:124
$x_tick_pos
Definition: phplot.php:129
SetPlotBgColor($which_color)
Definition: phplot.php:393
$error_bar_line_width
Definition: phplot.php:175
SetXLabelAngle($which_xla)
Definition: phplot.php:1658
SetDrawYGrid($dyg)
Definition: phplot.php:1808
SetNewPlotAreaPixels($x1, $y1, $x2, $y2)
Definition: phplot.php:4998
SetErrorBarLineWidth($which_seblw)
Definition: phplot.php:1959
SetPointSizes($which_ps)
Definition: phplot.php:2030
$dashed_grid
Definition: phplot.php:147
DrawGraph()
Definition: phplot.php:4653
SetLegendPixels($which_x, $which_y)
Definition: phplot.php:1733
SetRGBColor($color_asked)
Definition: phplot.php:557
SetDataValues(&$which_dv)
Definition: phplot.php:2100
$dashed_style
Definition: phplot.php:168
$shading
Definition: phplot.php:180
SetTTFPath($which_path)
Definition: phplot.php:765
$point_sizes
Definition: phplot.php:170
CalcTranslation()
Definition: phplot.php:2866
$browser_cache
Definition: phplot.php:34
DrawArea()
Definition: phplot.php:4254
tile_img($file, $xorig, $yorig, $width, $height, $mode)
Definition: phplot.php:3287
ProcessTextTTF($draw_it, $font_file, $font_size, $angle, $x, $y, $color, $text, $h_factor, $v_factor)
Definition: phplot.php:1108
SetPlotBorderType($pbt)
Definition: phplot.php:1779
SetFont($which_elem, $which_font, $which_size=12)
Definition: phplot.php:825
FormatLabel($which_pos, $which_lab)
Definition: phplot.php:3077
SetXLabel($which_xlab)
Definition: phplot.php:4901
$y_tick_cross
Definition: phplot.php:127
$line_styles
Definition: phplot.php:167
DrawTitle()
Definition: phplot.php:3366
$tick_color
Definition: phplot.php:160
DrawYAxis()
Definition: phplot.php:3440
SetDrawXGrid($dxg)
Definition: phplot.php:1818
SetShading($which_s)
Definition: phplot.php:1898
SetYTimeFormat($which_ytf)
Definition: phplot.php:1644
SetIsInline($which_ii)
Definition: phplot.php:1414
DrawXTitle()
Definition: phplot.php:3388
$image_border_type
Definition: phplot.php:178
SetTitleColor($which_color)
Definition: phplot.php:403
SetDrawPlotAreaBackground($dpab)
Definition: phplot.php:1798
$light_grid_color
Definition: phplot.php:159
CalcMaxDataLabelSize()
Definition: phplot.php:3049
SetDrawXDataLabels($which_dxdl)
Definition: phplot.php:4987
$use_ttf
Definition: phplot.php:46
SetDataBorderColors($which_br=NULL)
Definition: phplot.php:608
CalcPlotAreaWorld()
Definition: phplot.php:2707
CheckOption($which_opt, $which_acc, $which_func)
Definition: phplot.php:1683
DrawThinBarLines()
Definition: phplot.php:4106
DrawAxisLegend()
Definition: phplot.php:3886
DrawPlotAreaBackground()
Definition: phplot.php:3260
SetIndexColor($which_color)
Definition: phplot.php:317
PrintImage()
Definition: phplot.php:1424
$i_border
Definition: phplot.php:153
DrawDot($x_world, $y_world, $record, $color)
Definition: phplot.php:4181
DrawLines()
Definition: phplot.php:4319
$bar_width_adjust
Definition: phplot.php:66
DrawDotsError()
Definition: phplot.php:4023
SetCallback($reason, $function, $arg=NULL)
Definition: phplot.php:2194
DrawYTick($which_ylab, $which_ypix)
Definition: phplot.php:3479
SetSkipBottomTick($skip)
Definition: phplot.php:3185
SizeText($which_font, $which_angle, $which_text)
Definition: phplot.php:1333
SetUseTTF($which_ttf)
Definition: phplot.php:756
number_format($number, $decimals=0)
Definition: phplot.php:2165
SetYTickIncrement($which_ti='')
Definition: phplot.php:3128
ProcessTextGD($draw_it, $font_number, $font_width, $font_height, $angle, $x, $y, $color, $text, $h_factor, $v_factor)
Definition: phplot.php:1003
SetColor($which_color)
Definition: phplot.php:5015
FindDataLimits()
Definition: phplot.php:2266
SetVertTickPosition($which_tp)
Definition: phplot.php:4844
DrawLinesError()
Definition: phplot.php:4384
DrawSquared()
Definition: phplot.php:4451
DrawDots()
Definition: phplot.php:4068
SetDefaultDashedStyle($which_style)
Definition: phplot.php:662
$title_color
Definition: phplot.php:161
SetDataColors($which_data=NULL, $which_border=NULL)
Definition: phplot.php:580
SetDrawVertTicks($which_dvt)
Definition: phplot.php:4791
SetLineWidths($which_lw=NULL)
Definition: phplot.php:706
SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm)
Definition: phplot.php:2665
SetLegendWorld($which_x, $which_y)
Definition: phplot.php:1750
$y_label_type
Definition: phplot.php:104
$x_tick_cross
Definition: phplot.php:126
DoCallback() # Note
Definition: phplot.php:2241
$skip_right_tick
Definition: phplot.php:141
SetTransparentColor($which_color)
Definition: phplot.php:480
$text_color
Definition: phplot.php:157
SetYTitle($which_ytitle, $which_ypos='plotleft')
Definition: phplot.php:1883
SetXLabelFontSize($which_size)
Definition: phplot.php:4885
PrintError($error_message)
Definition: phplot.php:1506
SetImageBorderColor($which_color)
Definition: phplot.php:468
SetPrecisionX($which_prec)
Definition: phplot.php:1945
$y_title_pos
Definition: phplot.php:80
SetNumberFormat($decimal_point, $thousands_sep)
Definition: phplot.php:1650
$skip_bottom_tick
Definition: phplot.php:139
DrawPlotBorder()
Definition: phplot.php:3641
SetDefaultStyles()
Definition: phplot.php:347
$grid_color
Definition: phplot.php:158
$y_tick_pos
Definition: phplot.php:130
$draw_x_grid
Definition: phplot.php:144
GetCallback($reason)
Definition: phplot.php:2211
SetYTitlePos($xpos)
Definition: phplot.php:4967
$plot_bg_color
Definition: phplot.php:154
SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL)
Definition: phplot.php:2772
SetXGridLabelPos($which_xglp)
Definition: phplot.php:4949
$x_tick_length
Definition: phplot.php:123
$title_txt
Definition: phplot.php:74
SetYLabelAngle($which_yla)
Definition: phplot.php:1664
SetBrowserCache($which_browser_cache)
Definition: phplot.php:1699
SetSkipRightTick($skip)
Definition: phplot.php:3203
SetXDataLabelAngle($which_xdla)
Definition: phplot.php:4976
SetVertTickIncrement($inc)
Definition: phplot.php:4836
$error_bar_size
Definition: phplot.php:173
$draw_y_data_label_lines
Definition: phplot.php:100
$point_shapes
Definition: phplot.php:171
SetXGridLabelType($which_xglt)
Definition: phplot.php:4935
PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
Definition: phplot.php:212
$bar_extra_space
Definition: phplot.php:65
$x_precision
Definition: phplot.php:69
DrawXDataLine($xpos, $row)
Definition: phplot.php:3743
SetErrorBarSize($which_ebs)
Definition: phplot.php:1972
SetLegendStyle($text_align, $colorbox_align='', $style='')
Definition: phplot.php:1766
SetLabelScalePosition($which_blp)
Definition: phplot.php:1965
DrawLegend()
Definition: phplot.php:3776
$default_ttfont
Definition: phplot.php:48
SetBgImage($input_file, $mode='centeredtile')
Definition: phplot.php:1380
SetImageBorderType($sibt)
Definition: phplot.php:1788
SetPrecisionY($which_prec)
Definition: phplot.php:1952
CalcBarWidths()
Definition: phplot.php:2785
$yscale_type
Definition: phplot.php:43
CalcTicks($which)
Definition: phplot.php:2957
$y_time_format
Definition: phplot.php:106
SetFileFormat($format)
Definition: phplot.php:1348
$file_format
Definition: phplot.php:56
SetGridColor($which_color)
Definition: phplot.php:457
$error_bar_colors
Definition: phplot.php:163
SetRGBArray($which_color_array)
Definition: phplot.php:497
DrawYTitle()
Definition: phplot.php:3414
$x_data_label_pos
Definition: phplot.php:96
SetDrawYDataLabelLines($dydl)
Definition: phplot.php:1849
SetNumXTicks($which_nt)
Definition: phplot.php:3138
$line_widths
Definition: phplot.php:166
$draw_x_data_label_lines
Definition: phplot.php:99
$plot_type
Definition: phplot.php:61
SetPlotType($which_pt)
Definition: phplot.php:1904
$xscale_type
Definition: phplot.php:42
SetXTickPos($which_tp)
Definition: phplot.php:3167
SetDefaultTTFont($which_font)
Definition: phplot.php:783
CalcPlotAreaPixels()
Definition: phplot.php:2647
$num_y_ticks
Definition: phplot.php:133
SetXDataLabelPos($which_xdlp)
Definition: phplot.php:1554
SetPrintImage($which_pi)
Definition: phplot.php:1708
$bg_color
Definition: phplot.php:155
$skip_left_tick
Definition: phplot.php:140
CalcMaxTickLabelSize($which)
Definition: phplot.php:3009
$draw_plot_area_background
Definition: phplot.php:182
SetNumYTicks($which_nt)
Definition: phplot.php:3147
SetErrorBarShape($which_ebs)
Definition: phplot.php:1982
DrawImageBorder()
Definition: phplot.php:3329
pad_array(&$arr, $size)
Definition: phplot.php:2150
SetDashedStyle($which_ndxcol)
Definition: phplot.php:695
SetYScaleType($which_yst)
Definition: phplot.php:1939
PadArrays()
Definition: phplot.php:2126
SetLightGridColor($which_color)
Definition: phplot.php:446
SetOutputFile($which_output_file)
Definition: phplot.php:1404
$callbacks
Definition: phplot.php:186
SetTickLength($which_tl)
Definition: phplot.php:4917
SetPlotAreaBgImage($input_file, $mode='tile')
Definition: phplot.php:1393
$x_label_type
Definition: phplot.php:103
$y_data_label_pos
Definition: phplot.php:97
DrawDataLabel($which_font, $which_angle, $x_world, $y_world, $which_color, $which_text, $which_halign='center', $which_valign='bottom', $x_adjustment=0, $y_adjustment=0)
Definition: phplot.php:3678
SetPointSize($which_ps)
Definition: phplot.php:5047
SetTitle($which_title)
Definition: phplot.php:1859
$x_label_angle
Definition: phplot.php:52
$x_time_format
Definition: phplot.php:105
DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text, $which_halign='left', $which_valign='bottom')
Definition: phplot.php:1314
SetDefaultFonts()
Definition: phplot.php:792
$legend
Definition: phplot.php:114
SetAxisFontSize($which_size)
Definition: phplot.php:4868
DrawBackground()
Definition: phplot.php:3241
DrawXAxis()
Definition: phplot.php:3455
SetYTickLabelPos($which_ytlp)
Definition: phplot.php:1612
SetXTitle($which_xtitle, $which_xpos='plotdown')
Definition: phplot.php:1868
$label_color
Definition: phplot.php:156
SetYTickLength($which_yln)
Definition: phplot.php:3215
$y_label_angle
Definition: phplot.php:53
$error_bar_shape
Definition: phplot.php:174
SetYDataLabelPos($which_ydlp, $which_distance_from_point=0)
Definition: phplot.php:1571
error($a_errmsg)
set error message @access public
exit
Definition: login.php:54
$path
Definition: index.php:22