| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php // $Id: graphlib.php,v 1.20.10.3 2008/06/11 08:22:35 jmg324 Exp $ 2 3 /* 4 Graph Class. PHP Class to draw line, point, bar, and area graphs, including numeric x-axis and double y-axis. 5 Version: 1.6.3 6 Copyright (C) 2000 Herman Veluwenkamp 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22 Copy of GNU Lesser General Public License at: http://www.gnu.org/copyleft/lesser.txt 23 Contact author at: hermanV@mindless.com 24 */ 25 26 /* This file contains modifications by Martin Dougiamas 27 * as part of Moodle (http://moodle.com). Modified lines 28 * are marked with "Moodle". 29 */ 30 31 32 class graph { 33 var $image; 34 var $debug = FALSE; // be careful!! 35 var $calculated = array(); // array of computed values for chart 36 var $parameter = array( // input parameters 37 'width' => 320, // default width of image 38 'height' => 240, // default height of image 39 'file_name' => 'none', // name of file for file to be saved as. 40 // NOTE: no suffix required. this is determined from output_format below. 41 'output_format' => 'PNG', // image output format. 'GIF', 'PNG', 'JPEG'. default 'PNG'. 42 43 'seconds_to_live' => 0, // expiry time in seconds (for HTTP header) 44 'hours_to_live' => 0, // expiry time in hours (for HTTP header) 45 'path_to_fonts' => 'fonts/', // path to fonts folder. don't forget *trailing* slash!! 46 // for WINDOZE this may need to be the full path, not relative. 47 48 'title' => 'Graph Title', // text for graph title 49 'title_font' => 'default.ttf', // title text font. don't forget to set 'path_to_fonts' above. 50 'title_size' => 16, // title text point size 51 'title_colour' => 'black', // colour for title text 52 53 'x_label' => '', // if this is set then this text is printed on bottom axis of graph. 54 'y_label_left' => '', // if this is set then this text is printed on left axis of graph. 55 'y_label_right' => '', // if this is set then this text is printed on right axis of graph. 56 57 'label_size' => 8, // label text point size 58 'label_font' => 'default.ttf', // label text font. don't forget to set 'path_to_fonts' above. 59 'label_colour' => 'gray33', // label text colour 60 'y_label_angle' => 90, // rotation of y axis label 61 62 'x_label_angle' => 90, // rotation of y axis label 63 64 'outer_padding' => 5, // padding around outer text. i.e. title, y label, and x label. 65 'inner_padding' => 0, // padding beteen axis text and graph. 66 'x_inner_padding' => 5, // padding beteen axis text and graph. 67 'y_inner_padding' => 6, // padding beteen axis text and graph. 68 'outer_border' => 'none', // colour of border aound image, or 'none'. 69 'inner_border' => 'black', // colour of border around actual graph, or 'none'. 70 'inner_border_type' => 'box', // 'box' for all four sides, 'axis' for x/y axis only, 71 // 'y' or 'y-left' for y axis only, 'y-right' for right y axis only, 72 // 'x' for x axis only, 'u' for both left and right y axis and x axis. 73 'outer_background' => 'none', // background colour of entire image. 74 'inner_background' => 'none', // background colour of plot area. 75 76 'y_min_left' => 0, // this will be reset to minimum value if there is a value lower than this. 77 'y_max_left' => 0, // this will be reset to maximum value if there is a value higher than this. 78 'y_min_right' => 0, // this will be reset to minimum value if there is a value lower than this. 79 'y_max_right' => 0, // this will be reset to maximum value if there is a value higher than this. 80 'x_min' => 0, // only used if x axis is numeric. 81 'x_max' => 0, // only used if x axis is numeric. 82 83 'y_resolution_left' => 1, // scaling for rounding of y axis max value. 84 // if max y value is 8645 then 85 // if y_resolution is 0, then y_max becomes 9000. 86 // if y_resolution is 1, then y_max becomes 8700. 87 // if y_resolution is 2, then y_max becomes 8650. 88 // if y_resolution is 3, then y_max becomes 8645. 89 // get it? 90 'y_decimal_left' => 0, // number of decimal places for y_axis text. 91 'y_resolution_right' => 2, // ... same for right hand side 92 'y_decimal_right' => 0, // ... same for right hand side 93 'x_resolution' => 2, // only used if x axis is numeric. 94 'x_decimal' => 0, // only used if x axis is numeric. 95 96 'point_size' => 4, // default point size. use even number for diamond or triangle to get nice look. 97 'brush_size' => 4, // default brush size for brush line. 98 'brush_type' => 'circle', // type of brush to use to draw line. choose from the following 99 // 'circle', 'square', 'horizontal', 'vertical', 'slash', 'backslash' 100 'bar_size' => 0.8, // size of bar to draw. <1 bars won't touch 101 // 1 is full width - i.e. bars will touch. 102 // >1 means bars will overlap. 103 'bar_spacing' => 10, // space in pixels between group of bars for each x value. 104 'shadow_offset' => 3, // draw shadow at this offset, unless overidden by data parameter. 105 'shadow' => 'grayCC', // 'none' or colour of shadow. 106 'shadow_below_axis' => true, // whether to draw shadows of bars and areas below the x/zero axis. 107 108 109 'x_axis_gridlines' => 'auto', // if set to a number then x axis is treated as numeric. 110 'y_axis_gridlines' => 6, // number of gridlines on y axis. 111 'zero_axis' => 'none', // colour to draw zero-axis, or 'none'. 112 113 114 'axis_font' => 'default.ttf', // axis text font. don't forget to set 'path_to_fonts' above. 115 'axis_size' => 8, // axis text font size in points 116 'axis_colour' => 'gray33', // colour of axis text. 117 'y_axis_angle' => 0, // rotation of axis text. 118 'x_axis_angle' => 0, // rotation of axis text. 119 120 'y_axis_text_left' => 1, // whether to print left hand y axis text. if 0 no text, if 1 all ticks have text, 121 'x_axis_text' => 1, // if 4 then print every 4th tick and text, etc... 122 'y_axis_text_right' => 0, // behaviour same as above for right hand y axis. 123 124 'x_offset' => 0.5, // x axis tick offset from y axis as fraction of tick spacing. 125 'y_ticks_colour' => 'black', // colour to draw y ticks, or 'none' 126 'x_ticks_colour' => 'black', // colour to draw x ticks, or 'none' 127 'y_grid' => 'line', // grid lines. set to 'line' or 'dash'... 128 'x_grid' => 'line', // or if set to 'none' print nothing. 129 'grid_colour' => 'grayEE', // default grid colour. 130 'tick_length' => 4, // length of ticks in pixels. can be negative. i.e. outside data drawing area. 131 132 'legend' => 'none', // default. no legend. 133 // otherwise: 'top-left', 'top-right', 'bottom-left', 'bottom-right', 134 // 'outside-top', 'outside-bottom', 'outside-left', or 'outside-right'. 135 'legend_offset' => 10, // offset in pixels from graph or outside border. 136 'legend_padding' => 5, // padding around legend text. 137 'legend_font' => 'default.ttf', // legend text font. don't forget to set 'path_to_fonts' above. 138 'legend_size' => 8, // legend text point size. 139 'legend_colour' => 'black', // legend text colour. 140 'legend_border' => 'none', // legend border colour, or 'none'. 141 142 'decimal_point' => '.', // symbol for decimal separation '.' or ',' *european support. 143 'thousand_sep' => ',', // symbol for thousand separation ',' or '' 144 145 ); 146 var $y_tick_labels = null; // array of text values for y-axis tick labels 147 var $offset_relation = null; // array of offsets for different sets of data 148 149 150 151 // init all text - title, labels, and axis text. 152 function init() { 153 154 /// Moodle mods: overrides the font path and encodings 155 156 global $CFG; 157 158 /// A default.ttf is searched for in this order: 159 /// dataroot/lang/xx_local/fonts 160 /// dataroot/lang/xx/fonts 161 /// dirroot/lang/xx/fonts 162 /// dataroot/lang 163 /// lib/ 164 165 $currlang = current_language(); 166 if (file_exists("$CFG->dataroot/lang/".$currlang."_local/fonts/default.ttf")) { 167 $fontpath = "$CFG->dataroot/lang/".$currlang."_local/fonts/"; 168 } else if (file_exists("$CFG->dataroot/lang/$currlang/fonts/default.ttf")) { 169 $fontpath = "$CFG->dataroot/lang/$currlang/fonts/"; 170 } else if (file_exists("$CFG->dirroot/lang/$currlang/fonts/default.ttf")) { 171 $fontpath = "$CFG->dirroot/lang/$currlang/fonts/"; 172 } else if (file_exists("$CFG->dataroot/lang/default.ttf")) { 173 $fontpath = "$CFG->dataroot/lang/"; 174 } else { 175 $fontpath = "$CFG->libdir/"; 176 } 177 178 $this->parameter['path_to_fonts'] = $fontpath; 179 180 /// End Moodle mods 181 182 183 184 $this->calculated['outer_border'] = $this->calculated['boundary_box']; 185 186 // outer padding 187 $this->calculated['boundary_box']['left'] += $this->parameter['outer_padding']; 188 $this->calculated['boundary_box']['top'] += $this->parameter['outer_padding']; 189 $this->calculated['boundary_box']['right'] -= $this->parameter['outer_padding']; 190 $this->calculated['boundary_box']['bottom'] -= $this->parameter['outer_padding']; 191 192 $this->init_x_axis(); 193 $this->init_y_axis(); 194 $this->init_legend(); 195 $this->init_labels(); 196 197 // take into account tick lengths 198 $this->calculated['bottom_inner_padding'] = $this->parameter['x_inner_padding']; 199 if (($this->parameter['x_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) 200 $this->calculated['bottom_inner_padding'] -= $this->parameter['tick_length']; 201 $this->calculated['boundary_box']['bottom'] -= $this->calculated['bottom_inner_padding']; 202 203 $this->calculated['left_inner_padding'] = $this->parameter['y_inner_padding']; 204 if ($this->parameter['y_axis_text_left']) { 205 if (($this->parameter['y_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) 206 $this->calculated['left_inner_padding'] -= $this->parameter['tick_length']; 207 } 208 $this->calculated['boundary_box']['left'] += $this->calculated['left_inner_padding']; 209 210 $this->calculated['right_inner_padding'] = $this->parameter['y_inner_padding']; 211 if ($this->parameter['y_axis_text_right']) { 212 if (($this->parameter['y_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) 213 $this->calculated['right_inner_padding'] -= $this->parameter['tick_length']; 214 } 215 $this->calculated['boundary_box']['right'] -= $this->calculated['right_inner_padding']; 216 217 // boundaryBox now has coords for plotting area. 218 $this->calculated['inner_border'] = $this->calculated['boundary_box']; 219 220 $this->init_data(); 221 $this->init_x_ticks(); 222 $this->init_y_ticks(); 223 } 224 225 function draw_text() { 226 $colour = $this->parameter['outer_background']; 227 if ($colour != 'none') $this->draw_rectangle($this->calculated['outer_border'], $colour, 'fill'); // graph background 228 229 // draw border around image 230 $colour = $this->parameter['outer_border']; 231 if ($colour != 'none') $this->draw_rectangle($this->calculated['outer_border'], $colour, 'box'); // graph border 232 233 $this->draw_title(); 234 $this->draw_x_label(); 235 $this->draw_y_label_left(); 236 $this->draw_y_label_right(); 237 $this->draw_x_axis(); 238 $this->draw_y_axis(); 239 if ($this->calculated['y_axis_left']['has_data']) $this->draw_zero_axis_left(); // either draw zero axis on left 240 else if ($this->calculated['y_axis_right']['has_data']) $this->draw_zero_axis_right(); // ... or right. 241 $this->draw_legend(); 242 243 // draw border around plot area 244 $colour = $this->parameter['inner_background']; 245 if ($colour != 'none') $this->draw_rectangle($this->calculated['inner_border'], $colour, 'fill'); // graph background 246 247 // draw border around image 248 $colour = $this->parameter['inner_border']; 249 if ($colour != 'none') $this->draw_rectangle($this->calculated['inner_border'], $colour, $this->parameter['inner_border_type']); // graph border 250 } 251 252 function draw_stack() { 253 $this->init(); 254 $this->draw_text(); 255 256 $yOrder = $this->y_order; // save y_order data. 257 // iterate over each data set. order is very important if you want to see data correctly. remember shadows!! 258 foreach ($yOrder as $set) { 259 $this->y_order = array($set); 260 $this->init_data(); 261 $this->draw_data(); 262 } 263 $this->y_order = $yOrder; // revert y_order data. 264 265 $this->output(); 266 } 267 268 function draw() { 269 $this->init(); 270 $this->draw_text(); 271 $this->draw_data(); 272 $this->output(); 273 } 274 275 // draw a data set 276 function draw_set($order, $set, $offset) { 277 if ($offset) @$this->init_variable($colour, $this->y_format[$set]['shadow'], $this->parameter['shadow']); 278 else $colour = $this->y_format[$set]['colour']; 279 @$this->init_variable($point, $this->y_format[$set]['point'], 'none'); 280 @$this->init_variable($pointSize, $this->y_format[$set]['point_size'], $this->parameter['point_size']); 281 @$this->init_variable($line, $this->y_format[$set]['line'], 'none'); 282 @$this->init_variable($brushType, $this->y_format[$set]['brush_type'], $this->parameter['brush_type']); 283 @$this->init_variable($brushSize, $this->y_format[$set]['brush_size'], $this->parameter['brush_size']); 284 @$this->init_variable($bar, $this->y_format[$set]['bar'], 'none'); 285 @$this->init_variable($barSize, $this->y_format[$set]['bar_size'], $this->parameter['bar_size']); 286 @$this->init_variable($area, $this->y_format[$set]['area'], 'none'); 287 288 $lastX = 0; 289 $lastY = 'none'; 290 $fromX = 0; 291 $fromY = 'none'; 292 293 //print "set $set<br />"; 294 //expand_pre($this->calculated['y_plot']); 295 296 foreach ($this->x_data as $index => $x) { 297 //print "index $index<br />"; 298 $thisY = $this->calculated['y_plot'][$set][$index]; 299 $thisX = $this->calculated['x_plot'][$index]; 300 301 //print "$thisX, $thisY <br />"; 302 303 if (($bar!='none') && (string)$thisY != 'none') { 304 if ($relatedset = $this->offset_relation[$set]) { // Moodle 305 $yoffset = $this->calculated['y_plot'][$relatedset][$index]; // Moodle 306 } else { // Moodle 307 $yoffset = 0; // Moodle 308 } // Moodle 309 //$this->bar($thisX, $thisY, $bar, $barSize, $colour, $offset, $set); // Moodle 310 $this->bar($thisX, $thisY, $bar, $barSize, $colour, $offset, $set, $yoffset); // Moodle 311 } 312 313 if (($area!='none') && (((string)$lastY != 'none') && ((string)$thisY != 'none'))) 314 $this->area($lastX, $lastY, $thisX, $thisY, $area, $colour, $offset); 315 316 if (($point!='none') && (string)$thisY != 'none') $this->plot($thisX, $thisY, $point, $pointSize, $colour, $offset); 317 318 if (($line!='none') && ((string)$thisY != 'none')) { 319 if ((string)$fromY != 'none') 320 $this->line($fromX, $fromY, $thisX, $thisY, $line, $brushType, $brushSize, $colour, $offset); 321 322 $fromY = $thisY; // start next line from here 323 $fromX = $thisX; // ... 324 } else { 325 $fromY = 'none'; 326 $fromX = 'none'; 327 } 328 329 $lastX = $thisX; 330 $lastY = $thisY; 331 } 332 } 333 334 function draw_data() { 335 // cycle thru y data to be plotted 336 // first check for drop shadows... 337 foreach ($this->y_order as $order => $set) { 338 @$this->init_variable($offset, $this->y_format[$set]['shadow_offset'], $this->parameter['shadow_offset']); 339 @$this->init_variable($colour, $this->y_format[$set]['shadow'], $this->parameter['shadow']); 340 if ($colour != 'none') $this->draw_set($order, $set, $offset); 341 342 } 343 344 // then draw data 345 foreach ($this->y_order as $order => $set) { 346 $this->draw_set($order, $set, 0); 347 } 348 } 349 350 function draw_legend() { 351 $position = $this->parameter['legend']; 352 if ($position == 'none') return; // abort if no border 353 354 $borderColour = $this->parameter['legend_border']; 355 $offset = $this->parameter['legend_offset']; 356 $padding = $this->parameter['legend_padding']; 357 $height = $this->calculated['legend']['boundary_box_all']['height']; 358 $width = $this->calculated['legend']['boundary_box_all']['width']; 359 $graphTop = $this->calculated['boundary_box']['top']; 360 $graphBottom = $this->calculated['boundary_box']['bottom']; 361 $graphLeft = $this->calculated['boundary_box']['left']; 362 $graphRight = $this->calculated['boundary_box']['right']; 363 $outsideRight = $this->calculated['outer_border']['right']; 364 $outsideBottom = $this->calculated['outer_border']['bottom']; 365 switch ($position) { 366 case 'top-left': 367 $top = $graphTop + $offset; 368 $bottom = $graphTop + $height + $offset; 369 $left = $graphLeft + $offset; 370 $right = $graphLeft + $width + $offset; 371 372 break; 373 case 'top-right': 374 $top = $graphTop + $offset; 375 $bottom = $graphTop + $height + $offset; 376 $left = $graphRight - $width - $offset; 377 $right = $graphRight - $offset; 378 379 break; 380 case 'bottom-left': 381 $top = $graphBottom - $height - $offset; 382 $bottom = $graphBottom - $offset; 383 $left = $graphLeft + $offset; 384 $right = $graphLeft + $width + $offset; 385 386 break; 387 case 'bottom-right': 388 $top = $graphBottom - $height - $offset; 389 $bottom = $graphBottom - $offset; 390 $left = $graphRight - $width - $offset; 391 $right = $graphRight - $offset; 392 break; 393 394 case 'outside-top' : 395 $top = $graphTop; 396 $bottom = $graphTop + $height; 397 $left = $outsideRight - $width - $offset; 398 $right = $outsideRight - $offset; 399 break; 400 401 case 'outside-bottom' : 402 $top = $graphBottom - $height; 403 $bottom = $graphBottom; 404 $left = $outsideRight - $width - $offset; 405 $right = $outsideRight - $offset; 406 break; 407 408 case 'outside-left' : 409 $top = $outsideBottom - $height - $offset; 410 $bottom = $outsideBottom - $offset; 411 $left = $graphLeft; 412 $right = $graphLeft + $width; 413 break; 414 415 case 'outside-right' : 416 $top = $outsideBottom - $height - $offset; 417 $bottom = $outsideBottom - $offset; 418 $left = $graphRight - $width; 419 $right = $graphRight; 420 break; 421 default: // default is top left. no particular reason. 422 $top = $this->calculated['boundary_box']['top']; 423 $bottom = $this->calculated['boundary_box']['top'] + $this->calculated['legend']['boundary_box_all']['height']; 424 $left = $this->calculated['boundary_box']['left']; 425 $right = $this->calculated['boundary_box']['right'] + $this->calculated['legend']['boundary_box_all']['width']; 426 427 } 428 // legend border 429 if($borderColour!='none') $this->draw_rectangle(array('top' => $top, 430 'left' => $left, 431 'bottom' => $bottom, 432 'right' => $right), $this->parameter['legend_border'], 'box'); 433 434 // legend text 435 $legendText = array('points' => $this->parameter['legend_size'], 436 'angle' => 0, 437 'font' => $this->parameter['legend_font'], 438 'colour' => $this->parameter['legend_colour']); 439 440 $box = $this->calculated['legend']['boundary_box_max']['height']; // use max height for legend square size. 441 $x = $left + $padding; 442 $x_text = $x + $box * 2; 443 $y = $top + $padding; 444 445 foreach ($this->y_order as $set) { 446 $legendText['text'] = $this->calculated['legend']['text'][$set]; 447 if ($legendText['text'] != 'none') { 448 // if text exists then draw box and text 449 $boxColour = $this->colour[$this->y_format[$set]['colour']]; 450 451 // draw box 452 ImageFilledRectangle($this->image, $x, $y, $x + $box, $y + $box, $boxColour); 453 454 // draw text 455 $coords = array('x' => $x + $box * 2, 'y' => $y, 'reference' => 'top-left'); 456 $legendText['boundary_box'] = $this->calculated['legend']['boundary_box'][$set]; 457 $this->update_boundaryBox($legendText['boundary_box'], $coords); 458 $this->print_TTF($legendText); 459 $y += $padding + $box; 460 } 461 } 462 463 } 464 465 function draw_y_label_right() { 466 if (!$this->parameter['y_label_right']) return; 467 $x = $this->calculated['boundary_box']['right'] + $this->parameter['y_inner_padding']; 468 if ($this->parameter['y_axis_text_right']) $x += $this->calculated['y_axis_right']['boundary_box_max']['width'] 469 + $this->calculated['right_inner_padding']; 470 $y = ($this->calculated['boundary_box']['bottom'] + $this->calculated['boundary_box']['top']) / 2; 471 472 $label = $this->calculated['y_label_right']; 473 $coords = array('x' => $x, 'y' => $y, 'reference' => 'left-center'); 474 $this->update_boundaryBox($label['boundary_box'], $coords); 475 $this->print_TTF($label); 476 } 477 478 479 function draw_y_label_left() { 480 if (!$this->parameter['y_label_left']) return; 481 $x = $this->calculated['boundary_box']['left'] - $this->parameter['y_inner_padding']; 482 if ($this->parameter['y_axis_text_left']) $x -= $this->calculated['y_axis_left']['boundary_box_max']['width'] 483 + $this->calculated['left_inner_padding']; 484 $y = ($this->calculated['boundary_box']['bottom'] + $this->calculated['boundary_box']['top']) / 2; 485 486 $label = $this->calculated['y_label_left']; 487 $coords = array('x' => $x, 'y' => $y, 'reference' => 'right-center'); 488 $this->update_boundaryBox($label['boundary_box'], $coords); 489 $this->print_TTF($label); 490 } 491 492 function draw_title() { 493 if (!$this->parameter['title']) return; 494 //$y = $this->calculated['outside_border']['top'] + $this->parameter['outer_padding']; 495 $y = $this->calculated['boundary_box']['top'] - $this->parameter['outer_padding']; 496 $x = ($this->calculated['boundary_box']['right'] + $this->calculated['boundary_box']['left']) / 2; 497 $label = $this->calculated['title']; 498 $coords = array('x' => $x, 'y' => $y, 'reference' => 'bottom-center'); 499 $this->update_boundaryBox($label['boundary_box'], $coords); 500 $this->print_TTF($label); 501 } 502 503 function draw_x_label() { 504 if (!$this->parameter['x_label']) return; 505 $y = $this->calculated['boundary_box']['bottom'] + $this->parameter['x_inner_padding']; 506 if ($this->parameter['x_axis_text']) $y += $this->calculated['x_axis']['boundary_box_max']['height'] 507 + $this->calculated['bottom_inner_padding']; 508 $x = ($this->calculated['boundary_box']['right'] + $this->calculated['boundary_box']['left']) / 2; 509 $label = $this->calculated['x_label']; 510 $coords = array('x' => $x, 'y' => $y, 'reference' => 'top-center'); 511 $this->update_boundaryBox($label['boundary_box'], $coords); 512 $this->print_TTF($label); 513 } 514 515 function draw_zero_axis_left() { 516 $colour = $this->parameter['zero_axis']; 517 if ($colour == 'none') return; 518 // draw zero axis on left hand side 519 $this->calculated['zero_axis'] = round($this->calculated['boundary_box']['top'] + ($this->calculated['y_axis_left']['max'] * $this->calculated['y_axis_left']['factor'])); 520 ImageLine($this->image, $this->calculated['boundary_box']['left'], $this->calculated['zero_axis'], $this->calculated['boundary_box']['right'], $this->calculated['zero_axis'], $this->colour[$colour]); 521 } 522 523 function draw_zero_axis_right() { 524 $colour = $this->parameter['zero_axis']; 525 if ($colour == 'none') return; 526 // draw zero axis on right hand side 527 $this->calculated['zero_axis'] = round($this->calculated['boundary_box']['top'] + ($this->calculated['y_axis_right']['max'] * $this->calculated['y_axis_right']['factor'])); 528 ImageLine($this->image, $this->calculated['boundary_box']['left'], $this->calculated['zero_axis'], $this->calculated['boundary_box']['right'], $this->calculated['zero_axis'], $this->colour[$colour]); 529 } 530 531 function draw_x_axis() { 532 $gridColour = $this->colour[$this->parameter['grid_colour']]; 533 $tickColour = $this->colour[$this->parameter['x_ticks_colour']]; 534 $axis_colour = $this->parameter['axis_colour']; 535 $xGrid = $this->parameter['x_grid']; 536 $gridTop = $this->calculated['boundary_box']['top']; 537 $gridBottom = $this->calculated['boundary_box']['bottom']; 538 539 if ($this->parameter['tick_length'] >= 0) { 540 $tickTop = $this->calculated['boundary_box']['bottom'] - $this->parameter['tick_length']; 541 $tickBottom = $this->calculated['boundary_box']['bottom']; 542 $textBottom = $tickBottom + $this->calculated['bottom_inner_padding']; 543 } else { 544 $tickTop = $this->calculated['boundary_box']['bottom']; 545 $tickBottom = $this->calculated['boundary_box']['bottom'] - $this->parameter['tick_length']; 546 $textBottom = $tickBottom + $this->calculated['bottom_inner_padding']; 547 } 548 549 $axis_font = $this->parameter['axis_font']; 550 $axis_size = $this->parameter['axis_size']; 551 $axis_angle = $this->parameter['x_axis_angle']; 552 553 if ($axis_angle == 0) $reference = 'top-center'; 554 if ($axis_angle > 0) $reference = 'top-right'; 555 if ($axis_angle < 0) $reference = 'top-left'; 556 if ($axis_angle == 90) $reference = 'top-center'; 557 558 //generic tag information. applies to all axis text. 559 $axisTag = array('points' => $axis_size, 'angle' => $axis_angle, 'font' => $axis_font, 'colour' => $axis_colour); 560 561 foreach ($this->calculated['x_axis']['tick_x'] as $set => $tickX) { 562 // draw x grid if colour specified 563 if ($xGrid != 'none') { 564 switch ($xGrid) { 565 case 'line': 566 ImageLine($this->image, round($tickX), round($gridTop), round($tickX), round($gridBottom), $gridColour); 567 break; 568 case 'dash': 569 ImageDashedLine($this->image, round($tickX), round($gridTop), round($tickX), round($gridBottom), $gridColour); 570 break; 571 } 572 } 573 574 if ($this->parameter['x_axis_text'] && !($set % $this->parameter['x_axis_text'])) { // test if tick should be displayed 575 // draw tick 576 if ($tickColour != 'none') 577 ImageLine($this->image, round($tickX), round($tickTop), round($tickX), round($tickBottom), $tickColour); 578 579 // draw axis text 580 $coords = array('x' => $tickX, 'y' => $textBottom, 'reference' => $reference); 581 $axisTag['text'] = $this->calculated['x_axis']['text'][$set]; 582 $axisTag['boundary_box'] = $this->calculated['x_axis']['boundary_box'][$set]; 583 $this->update_boundaryBox($axisTag['boundary_box'], $coords); 584 $this->print_TTF($axisTag); 585 } 586 } 587 } 588 589 function draw_y_axis() { 590 $gridColour = $this->colour[$this->parameter['grid_colour']]; 591 $tickColour = $this->colour[$this->parameter['y_ticks_colour']]; 592 $axis_colour = $this->parameter['axis_colour']; 593 $yGrid = $this->parameter['y_grid']; 594 $gridLeft = $this->calculated['boundary_box']['left']; 595 $gridRight = $this->calculated['boundary_box']['right']; 596 597 // axis font information 598 $axis_font = $this->parameter['axis_font']; 599 $axis_size = $this->parameter['axis_size']; 600 $axis_angle = $this->parameter['y_axis_angle']; 601 $axisTag = array('points' => $axis_size, 'angle' => $axis_angle, 'font' => $axis_font, 'colour' => $axis_colour); 602 603 604 if ($this->calculated['y_axis_left']['has_data']) { 605 // LEFT HAND SIDE 606 // left and right coords for ticks 607 if ($this->parameter['tick_length'] >= 0) { 608 $tickLeft = $this->calculated['boundary_box']['left']; 609 $tickRight = $this->calculated['boundary_box']['left'] + $this->parameter['tick_length']; 610 } else { 611 $tickLeft = $this->calculated['boundary_box']['left'] + $this->parameter['tick_length']; 612 $tickRight = $this->calculated['boundary_box']['left']; 613 } 614 $textRight = $tickLeft - $this->calculated['left_inner_padding']; 615 616 if ($axis_angle == 0) $reference = 'right-center'; 617 if ($axis_angle > 0) $reference = 'right-top'; 618 if ($axis_angle < 0) $reference = 'right-bottom'; 619 if ($axis_angle == 90) $reference = 'right-center'; 620 621 foreach ($this->calculated['y_axis']['tick_y'] as $set => $tickY) { 622 // draw y grid if colour specified 623 if ($yGrid != 'none') { 624 switch ($yGrid) { 625 case 'line': 626 ImageLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 627 break; 628 case 'dash': 629 ImageDashedLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 630 break; 631 } 632 } 633 634 // y axis text 635 if ($this->parameter['y_axis_text_left'] && !($set % $this->parameter['y_axis_text_left'])) { // test if tick should be displayed 636 // draw tick 637 if ($tickColour != 'none') 638 ImageLine($this->image, round($tickLeft), round($tickY), round($tickRight), round($tickY), $tickColour); 639 640 // draw axis text... 641 $coords = array('x' => $textRight, 'y' => $tickY, 'reference' => $reference); 642 $axisTag['text'] = $this->calculated['y_axis_left']['text'][$set]; 643 $axisTag['boundary_box'] = $this->calculated['y_axis_left']['boundary_box'][$set]; 644 $this->update_boundaryBox($axisTag['boundary_box'], $coords); 645 $this->print_TTF($axisTag); 646 } 647 } 648 } 649 650 if ($this->calculated['y_axis_right']['has_data']) { 651 // RIGHT HAND SIDE 652 // left and right coords for ticks 653 if ($this->parameter['tick_length'] >= 0) { 654 $tickLeft = $this->calculated['boundary_box']['right'] - $this->parameter['tick_length']; 655 $tickRight = $this->calculated['boundary_box']['right']; 656 } else { 657 $tickLeft = $this->calculated['boundary_box']['right']; 658 $tickRight = $this->calculated['boundary_box']['right'] - $this->parameter['tick_length']; 659 } 660 $textLeft = $tickRight+ $this->calculated['left_inner_padding']; 661 662 if ($axis_angle == 0) $reference = 'left-center'; 663 if ($axis_angle > 0) $reference = 'left-bottom'; 664 if ($axis_angle < 0) $reference = 'left-top'; 665 if ($axis_angle == 90) $reference = 'left-center'; 666 667 foreach ($this->calculated['y_axis']['tick_y'] as $set => $tickY) { 668 if (!$this->calculated['y_axis_left']['has_data'] && $yGrid != 'none') { // draw grid if not drawn already (above) 669 switch ($yGrid) { 670 case 'line': 671 ImageLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 672 break; 673 case 'dash': 674 ImageDashedLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 675 break; 676 } 677 } 678 679 if ($this->parameter['y_axis_text_right'] && !($set % $this->parameter['y_axis_text_right'])) { // test if tick should be displayed 680 // draw tick 681 if ($tickColour != 'none') 682 ImageLine($this->image, round($tickLeft), round($tickY), round($tickRight), round($tickY), $tickColour); 683 684 // draw axis text... 685 $coords = array('x' => $textLeft, 'y' => $tickY, 'reference' => $reference); 686 $axisTag['text'] = $this->calculated['y_axis_right']['text'][$set]; 687 $axisTag['boundary_box'] = $this->calculated['y_axis_left']['boundary_box'][$set]; 688 $this->update_boundaryBox($axisTag['boundary_box'], $coords); 689 $this->print_TTF($axisTag); 690 } 691 } 692 } 693 } 694 695 function init_data() { 696 $this->calculated['y_plot'] = array(); // array to hold pixel plotting coords for y axis 697 $height = $this->calculated['boundary_box']['bottom'] - $this->calculated['boundary_box']['top']; 698 $width = $this->calculated['boundary_box']['right'] - $this->calculated['boundary_box']['left']; 699 700 // calculate pixel steps between axis ticks. 701 $this->calculated['y_axis']['step'] = $height / ($this->parameter['y_axis_gridlines'] - 1); 702 703 // calculate x ticks spacing taking into account x offset for ticks. 704 $extraTick = 2 * $this->parameter['x_offset']; // extra tick to account for padding 705 $numTicks = $this->calculated['x_axis']['num_ticks'] - 1; // number of x ticks 706 707 // Hack by rodger to avoid division by zero, see bug 1231 708 if ($numTicks==0) $numTicks=1; 709 710 $this->calculated['x_axis']['step'] = $width / ($numTicks + $extraTick); 711 $widthPlot = $width - ($this->calculated['x_axis']['step'] * $extraTick); 712 $this->calculated['x_axis']['step'] = $widthPlot / $numTicks; 713 714 //calculate factor for transforming x,y physical coords to logical coords for right hand y_axis. 715 $y_range = $this->calculated['y_axis_right']['max'] - $this->calculated['y_axis_right']['min']; 716 $y_range = ($y_range ? $y_range : 1); 717 $this->calculated['y_axis_right']['factor'] = $height / $y_range; 718 719 //calculate factor for transforming x,y physical coords to logical coords for left hand axis. 720 $yRange = $this->calculated['y_axis_left']['max'] - $this->calculated['y_axis_left']['min']; 721 $yRange = ($yRange ? $yRange : 1); 722 $this->calculated['y_axis_left']['factor'] = $height / $yRange; 723 if ($this->parameter['x_axis_gridlines'] != 'auto') { 724 $xRange = $this->calculated['x_axis']['max'] - $this->calculated['x_axis']['min']; 725 $xRange = ($xRange ? $xRange : 1); 726 $this->calculated['x_axis']['factor'] = $widthPlot / $xRange; 727 } 728 729 //expand_pre($this->calculated['boundary_box']); 730 // cycle thru all data sets... 731 $this->calculated['num_bars'] = 0; 732 foreach ($this->y_order as $order => $set) { 733 // determine how many bars there are 734 if (isset($this->y_format[$set]['bar']) && ($this->y_format[$set]['bar'] != 'none')) { 735 $this->calculated['bar_offset_index'][$set] = $this->calculated['num_bars']; // index to relate bar with data set. 736 $this->calculated['num_bars']++; 737 } 738 739 // calculate y coords for plotting data 740 foreach ($this->x_data as $index => $x) { 741 $this->calculated['y_plot'][$set][$index] = $this->y_data[$set][$index]; 742 743 if ((string)$this->y_data[$set][$index] != 'none') { 744 745 if (isset($this->y_format[$set]['y_axis']) && $this->y_format[$set]['y_axis'] == 'right') { 746 $this->calculated['y_plot'][$set][$index] = 747 round(($this->y_data[$set][$index] - $this->calculated['y_axis_right']['min']) 748 * $this->calculated['y_axis_right']['factor']); 749 } else { 750 //print "$set $index<br />"; 751 $this->calculated['y_plot'][$set][$index] = 752 round(($this->y_data[$set][$index] - $this->calculated['y_axis_left']['min']) 753 * $this->calculated['y_axis_left']['factor']); 754 } 755 756 } 757 } 758 } 759 //print "factor ".$this->calculated['x_axis']['factor']."<br />"; 760 //expand_pre($this->calculated['x_plot']); 761 762 // calculate bar parameters if bars are to be drawn. 763 if ($this->calculated['num_bars']) { 764 $xStep = $this->calculated['x_axis']['step']; 765 $totalWidth = $this->calculated['x_axis']['step'] - $this->parameter['bar_spacing']; 766 $barWidth = $totalWidth / $this->calculated['num_bars']; 767 768 $barX = ($barWidth - $totalWidth) / 2; // starting x offset 769 for ($i=0; $i < $this->calculated['num_bars']; $i++) { 770 $this->calculated['bar_offset_x'][$i] = $barX; 771 $barX += $barWidth; // add width of bar to x offset. 772 } 773 $this->calculated['bar_width'] = $barWidth; 774 } 775 776 777 } 778 779 function init_x_ticks() { 780 // get coords for x axis ticks and data plots 781 //$xGrid = $this->parameter['x_grid']; 782 $xStep = $this->calculated['x_axis']['step']; 783 $ticksOffset = $this->parameter['x_offset']; // where to start drawing ticks relative to y axis. 784 $gridLeft = $this->calculated['boundary_box']['left'] + ($xStep * $ticksOffset); // grid x start 785 $tickX = $gridLeft; // tick x coord 786 787 foreach ($this->calculated['x_axis']['text'] as $set => $value) { 788 //print "index: $set<br />"; 789 // x tick value 790 $this->calculated['x_axis']['tick_x'][$set] = $tickX; 791 // if num ticks is auto then x plot value is same as x tick 792 if ($this->parameter['x_axis_gridlines'] == 'auto') $this->calculated['x_plot'][$set] = round($tickX); 793 //print $this->calculated['x_plot'][$set].'<br />'; 794 $tickX += $xStep; 795 } 796 797 //print "xStep: $xStep <br />"; 798 // if numeric x axis then calculate x coords for each data point. this is seperate from x ticks. 799 $gridX = $gridLeft; 800 if (empty($this->calculated['x_axis']['factor'])) { 801 $this->calculated['x_axis']['factor'] = 0; 802 } 803 if (empty($this->calculated['x_axis']['min'])) { 804 $this->calculated['x_axis']['min'] = 0; 805 } 806 $factor = $this->calculated['x_axis']['factor']; 807 $min = $this->calculated['x_axis']['min']; 808 809 if ($this->parameter['x_axis_gridlines'] != 'auto') { 810 foreach ($this->x_data as $index => $x) { 811 //print "index: $index, x: $x<br />"; 812 $offset = $x - $this->calculated['x_axis']['min']; 813 814 //$gridX = ($offset * $this->calculated['x_axis']['factor']); 815 //print "offset: $offset <br />"; 816 //$this->calculated['x_plot'][$set] = $gridLeft + ($offset * $this->calculated['x_axis']['factor']); 817 818 $this->calculated['x_plot'][$index] = $gridLeft + ($x - $min) * $factor; 819 820 //print $this->calculated['x_plot'][$set].'<br />'; 821 } 822 } 823 //expand_pre($this->calculated['boundary_box']); 824 //print "factor ".$this->calculated['x_axis']['factor']."<br />"; 825 //expand_pre($this->calculated['x_plot']); 826 } 827 828 function init_y_ticks() { 829 // get coords for y axis ticks 830 831 $yStep = $this->calculated['y_axis']['step']; 832 $gridBottom = $this->calculated['boundary_box']['bottom']; 833 $tickY = $gridBottom; // tick y coord 834 835 for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { 836 $this->calculated['y_axis']['tick_y'][$i] = $tickY; 837 $tickY -= $yStep; 838 } 839 840 } 841 842 function init_labels() { 843 if ($this->parameter['title']) { 844 $size = $this->get_boundaryBox( 845 array('points' => $this->parameter['title_size'], 846 'angle' => 0, 847 'font' => $this->parameter['title_font'], 848 'text' => $this->parameter['title'])); 849 $this->calculated['title']['boundary_box'] = $size; 850 $this->calculated['title']['text'] = $this->parameter['title']; 851 $this->calculated['title']['font'] = $this->parameter['title_font']; 852 $this->calculated['title']['points'] = $this->parameter['title_size']; 853 $this->calculated['title']['colour'] = $this->parameter['title_colour']; 854 $this->calculated['title']['angle'] = 0; 855 856 $this->calculated['boundary_box']['top'] += $size['height'] + $this->parameter['outer_padding']; 857 //$this->calculated['boundary_box']['top'] += $size['height']; 858 859 } else $this->calculated['title']['boundary_box'] = $this->get_null_size(); 860 861 if ($this->parameter['y_label_left']) { 862 $this->calculated['y_label_left']['text'] = $this->parameter['y_label_left']; 863 $this->calculated['y_label_left']['angle'] = $this->parameter['y_label_angle']; 864 $this->calculated['y_label_left']['font'] = $this->parameter['label_font']; 865 $this->calculated['y_label_left']['points'] = $this->parameter['label_size']; 866 $this->calculated['y_label_left']['colour'] = $this->parameter['label_colour']; 867 868 $size = $this->get_boundaryBox($this->calculated['y_label_left']); 869 $this->calculated['y_label_left']['boundary_box'] = $size; 870 //$this->calculated['boundary_box']['left'] += $size['width'] + $this->parameter['inner_padding']; 871 $this->calculated['boundary_box']['left'] += $size['width']; 872 873 } else $this->calculated['y_label_left']['boundary_box'] = $this->get_null_size(); 874 875 if ($this->parameter['y_label_right']) { 876 $this->calculated['y_label_right']['text'] = $this->parameter['y_label_right']; 877 $this->calculated['y_label_right']['angle'] = $this->parameter['y_label_angle']; 878 $this->calculated['y_label_right']['font'] = $this->parameter['label_font']; 879 $this->calculated['y_label_right']['points'] = $this->parameter['label_size']; 880 $this->calculated['y_label_right']['colour'] = $this->parameter['label_colour']; 881 882 $size = $this->get_boundaryBox($this->calculated['y_label_right']); 883 $this->calculated['y_label_right']['boundary_box'] = $size; 884 //$this->calculated['boundary_box']['right'] -= $size['width'] + $this->parameter['inner_padding']; 885 $this->calculated['boundary_box']['right'] -= $size['width']; 886 887 } else $this->calculated['y_label_right']['boundary_box'] = $this->get_null_size(); 888 889 if ($this->parameter['x_label']) { 890 $this->calculated['x_label']['text'] = $this->parameter['x_label']; 891 $this->calculated['x_label']['angle'] = $this->parameter['x_label_angle']; 892 $this->calculated['x_label']['font'] = $this->parameter['label_font']; 893 $this->calculated['x_label']['points'] = $this->parameter['label_size']; 894 $this->calculated['x_label']['colour'] = $this->parameter['label_colour']; 895 896 $size = $this->get_boundaryBox($this->calculated['x_label']); 897 $this->calculated['x_label']['boundary_box'] = $size; 898 //$this->calculated['boundary_box']['bottom'] -= $size['height'] + $this->parameter['inner_padding']; 899 $this->calculated['boundary_box']['bottom'] -= $size['height']; 900 901 } else $this->calculated['x_label']['boundary_box'] = $this->get_null_size(); 902 903 } 904 905 906 function init_legend() { 907 $this->calculated['legend'] = array(); // array to hold calculated values for legend. 908 //$this->calculated['legend']['boundary_box_max'] = array('height' => 0, 'width' => 0); 909 $this->calculated['legend']['boundary_box_max'] = $this->get_null_size(); 910 if ($this->parameter['legend'] == 'none') return; 911 912 $position = $this->parameter['legend']; 913 $numSets = 0; // number of data sets with legends. 914 $sumTextHeight = 0; // total of height of all legend text items. 915 $width = 0; 916 $height = 0; 917 918 foreach ($this->y_order as $set) { 919 $text = isset($this->y_format[$set]['legend']) ? $this->y_format[$set]['legend'] : 'none'; 920 $size = $this->get_boundaryBox( 921 array('points' => $this->parameter['legend_size'], 922 'angle' => 0, 923 'font' => $this->parameter['legend_font'], 924 'text' => $text)); 925 926 $this->calculated['legend']['boundary_box'][$set] = $size; 927 $this->calculated['legend']['text'][$set] = $text; 928 //$this->calculated['legend']['font'][$set] = $this->parameter['legend_font']; 929 //$this->calculated['legend']['points'][$set] = $this->parameter['legend_size']; 930 //$this->calculated['legend']['angle'][$set] = 0; 931 932 if ($text && $text!='none') { 933 $numSets++; 934 $sumTextHeight += $size['height']; 935 } 936 937 if ($size['width'] > $this->calculated['legend']['boundary_box_max']['width']) 938 $this->calculated['legend']['boundary_box_max'] = $size; 939 } 940 941 $offset = $this->parameter['legend_offset']; // offset in pixels of legend box from graph border. 942 $padding = $this->parameter['legend_padding']; // padding in pixels around legend text. 943 $textWidth = $this->calculated['legend']['boundary_box_max']['width']; // width of largest legend item. 944 $textHeight = $this->calculated['legend']['boundary_box_max']['height']; // use height as size to use for colour square in legend. 945 $width = $padding * 2 + $textWidth + $textHeight * 2; // left and right padding + maximum text width + space for square 946 $height = $padding * ($numSets + 1) + $sumTextHeight; // top and bottom padding + padding between text + text. 947 948 949 $this->calculated['legend']['boundary_box_all'] = array('width' => $width, 950 'height' => $height, 951 'offset' => $offset, 952 'reference' => $position); 953 954 switch ($position) { // move in right or bottom if legend is outside data plotting area. 955 case 'outside-top' : 956 $this->calculated['boundary_box']['right'] -= $offset + $width; // move in right hand side 957 break; 958 959 case 'outside-bottom' : 960 $this->calculated['boundary_box']['right'] -= $offset + $width; // move in right hand side 961 break; 962 963 case 'outside-left' : 964 $this->calculated['boundary_box']['bottom'] -= $offset + $height; // move in right hand side 965 break; 966 967 case 'outside-right' : 968 $this->calculated['boundary_box']['bottom'] -= $offset + $height; // move in right hand side 969 break; 970 } 971 } 972 973 function init_y_axis() { 974 $this->calculated['y_axis_left'] = array(); // array to hold calculated values for y_axis on left. 975 $this->calculated['y_axis_left']['boundary_box_max'] = $this->get_null_size(); 976 $this->calculated['y_axis_right'] = array(); // array to hold calculated values for y_axis on right. 977 $this->calculated['y_axis_right']['boundary_box_max'] = $this->get_null_size(); 978 979 $axis_font = $this->parameter['axis_font']; 980 $axis_size = $this->parameter['axis_size']; 981 $axis_colour = $this->parameter['axis_colour']; 982 $axis_angle = $this->parameter['y_axis_angle']; 983 $y_tick_labels = $this->y_tick_labels; 984 985 $this->calculated['y_axis_left']['has_data'] = FALSE; 986 $this->calculated['y_axis_right']['has_data'] = FALSE; 987 988 // find min and max y values. 989 $minLeft = $this->parameter['y_min_left']; 990 $maxLeft = $this->parameter['y_max_left']; 991 $minRight = $this->parameter['y_min_right']; 992 $maxRight = $this->parameter['y_max_right']; 993 $dataLeft = array(); 994 $dataRight = array(); 995 foreach ($this->y_order as $order => $set) { 996 if (isset($this->y_format[$set]['y_axis']) && $this->y_format[$set]['y_axis'] == 'right') { 997 $this->calculated['y_axis_right']['has_data'] = TRUE; 998 $dataRight = array_merge($dataRight, $this->y_data[$set]); 999 } else { 1000 $this->calculated['y_axis_left']['has_data'] = TRUE; 1001 $dataLeft = array_merge($dataLeft, $this->y_data[$set]); 1002 } 1003 } 1004 $dataLeftRange = $this->find_range($dataLeft, $minLeft, $maxLeft, $this->parameter['y_resolution_left']); 1005 $dataRightRange = $this->find_range($dataRight, $minRight, $maxRight, $this->parameter['y_resolution_right']); 1006 $minLeft = $dataLeftRange['min']; 1007 $maxLeft = $dataLeftRange['max']; 1008 $minRight = $dataRightRange['min']; 1009 $maxRight = $dataRightRange['max']; 1010 1011 $this->calculated['y_axis_left']['min'] = $minLeft; 1012 $this->calculated['y_axis_left']['max'] = $maxLeft; 1013 $this->calculated['y_axis_right']['min'] = $minRight; 1014 $this->calculated['y_axis_right']['max'] = $maxRight; 1015 1016 $stepLeft = ($maxLeft - $minLeft) / ($this->parameter['y_axis_gridlines'] - 1); 1017 $startLeft = $minLeft; 1018 $step_right = ($maxRight - $minRight) / ($this->parameter['y_axis_gridlines'] - 1); 1019 $start_right = $minRight; 1020 1021 if ($this->parameter['y_axis_text_left']) { 1022 for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { // calculate y axis text sizes 1023 // left y axis 1024 if ($y_tick_labels) { 1025 $value = $y_tick_labels[$i]; 1026 } else { 1027 $value = number_format($startLeft, $this->parameter['y_decimal_left'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); 1028 } 1029 $this->calculated['y_axis_left']['data'][$i] = $startLeft; 1030 $this->calculated['y_axis_left']['text'][$i] = $value; // text is formatted raw data 1031 1032 $size = $this->get_boundaryBox( 1033 array('points' => $axis_size, 1034 'font' => $axis_font, 1035 'angle' => $axis_angle, 1036 'colour' => $axis_colour, 1037 'text' => $value)); 1038 $this->calculated['y_axis_left']['boundary_box'][$i] = $size; 1039 1040 if ($size['height'] > $this->calculated['y_axis_left']['boundary_box_max']['height']) 1041 $this->calculated['y_axis_left']['boundary_box_max']['height'] = $size['height']; 1042 if ($size['width'] > $this->calculated['y_axis_left']['boundary_box_max']['width']) 1043 $this->calculated['y_axis_left']['boundary_box_max']['width'] = $size['width']; 1044 1045 $startLeft += $stepLeft; 1046 } 1047 $this->calculated['boundary_box']['left'] += $this->calculated['y_axis_left']['boundary_box_max']['width'] 1048 + $this->parameter['y_inner_padding']; 1049 } 1050 1051 if ($this->parameter['y_axis_text_right']) { 1052 for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { // calculate y axis text sizes 1053 // right y axis 1054 $value = number_format($start_right, $this->parameter['y_decimal_right'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); 1055 $this->calculated['y_axis_right']['data'][$i] = $start_right; 1056 $this->calculated['y_axis_right']['text'][$i] = $value; // text is formatted raw data 1057 $size = $this->get_boundaryBox( 1058 array('points' => $axis_size, 1059 'font' => $axis_font, 1060 'angle' => $axis_angle, 1061 'colour' => $axis_colour, 1062 'text' => $value)); 1063 $this->calculated['y_axis_right']['boundary_box'][$i] = $size; 1064 1065 if ($size['height'] > $this->calculated['y_axis_right']['boundary_box_max']['height']) 1066 $this->calculated['y_axis_right']['boundary_box_max'] = $size; 1067 if ($size['width'] > $this->calculated['y_axis_right']['boundary_box_max']['width']) 1068 $this->calculated['y_axis_right']['boundary_box_max']['width'] = $size['width']; 1069 1070 $start_right += $step_right; 1071 } 1072 $this->calculated['boundary_box']['right'] -= $this->calculated['y_axis_right']['boundary_box_max']['width'] 1073 + $this->parameter['y_inner_padding']; 1074 } 1075 } 1076 1077 function init_x_axis() { 1078 $this->calculated['x_axis'] = array(); // array to hold calculated values for x_axis. 1079 $this->calculated['x_axis']['boundary_box_max'] = array('height' => 0, 'width' => 0); 1080 1081 $axis_font = $this->parameter['axis_font']; 1082 $axis_size = $this->parameter['axis_size']; 1083 $axis_colour = $this->parameter['axis_colour']; 1084 $axis_angle = $this->parameter['x_axis_angle']; 1085 1086 // check whether to treat x axis as numeric 1087 if ($this->parameter['x_axis_gridlines'] == 'auto') { // auto means text based x_axis, not numeric... 1088 $this->calculated['x_axis']['num_ticks'] = sizeof($this->x_data); 1089 $data = $this->x_data; 1090 for ($i=0; $i < $this->calculated['x_axis']['num_ticks']; $i++) { 1091 $value = array_shift($data); // grab value from begin of array 1092 $this->calculated['x_axis']['data'][$i] = $value; 1093 $this->calculated['x_axis']['text'][$i] = $value; // raw data and text are both the same in this case 1094 $size = $this->get_boundaryBox( 1095 array('points' => $axis_size, 1096 'font' => $axis_font, 1097 'angle' => $axis_angle, 1098 'colour' => $axis_colour, 1099 'text' => $value)); 1100 $this->calculated['x_axis']['boundary_box'][$i] = $size; 1101 if ($size['height'] > $this->calculated['x_axis']['boundary_box_max']['height']) 1102 $this->calculated['x_axis']['boundary_box_max'] = $size; 1103 } 1104 1105 } else { // x axis is numeric so find max min values... 1106 $this->calculated['x_axis']['num_ticks'] = $this->parameter['x_axis_gridlines']; 1107 1108 $min = $this->parameter['x_min']; 1109 $max = $this->parameter['x_max']; 1110 $data = array(); 1111 $data = $this->find_range($this->x_data, $min, $max, $this->parameter['x_resolution']); 1112 $min = $data['min']; 1113 $max = $data['max']; 1114 $this->calculated['x_axis']['min'] = $min; 1115 $this->calculated['x_axis']['max'] = $max; 1116 1117 $step = ($max - $min) / ($this->calculated['x_axis']['num_ticks'] - 1); 1118 $start = $min; 1119 1120 for ($i = 0; $i < $this->calculated['x_axis']['num_ticks']; $i++) { // calculate x axis text sizes 1121 $value = number_format($start, $this->parameter['xDecimal'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); 1122 $this->calculated['x_axis']['data'][$i] = $start; 1123 $this->calculated['x_axis']['text'][$i] = $value; // text is formatted raw data 1124 1125 $size = $this->get_boundaryBox( 1126 array('points' => $axis_size, 1127 'font' => $axis_font, 1128 'angle' => $axis_angle, 1129 'colour' => $axis_colour, 1130 'text' => $value)); 1131 $this->calculated['x_axis']['boundary_box'][$i] = $size; 1132 1133 if ($size['height'] > $this->calculated['x_axis']['boundary_box_max']['height']) 1134 $this->calculated['x_axis']['boundary_box_max'] = $size; 1135 1136 $start += $step; 1137 } 1138 } 1139 if ($this->parameter['x_axis_text']) 1140 $this->calculated['boundary_box']['bottom'] -= $this->calculated['x_axis']['boundary_box_max']['height'] 1141 + $this->parameter['x_inner_padding']; 1142 } 1143 1144 // find max and min values for a data array given the resolution. 1145 function find_range($data, $min, $max, $resolution) { 1146 if (sizeof($data) == 0 ) return array('min' => 0, 'max' => 0); 1147 foreach ($data as $key => $value) { 1148 if ($value=='none') continue; 1149 if ($value > $max) $max = $value; 1150 if ($value < $min) $min = $value; 1151 } 1152 1153 if ($max == 0) { 1154 $factor = 1; 1155 } else { 1156 if ($max < 0) $factor = - pow(10, (floor(log10(abs($max))) + $resolution) ); 1157 else $factor = pow(10, (floor(log10(abs($max))) - $resolution) ); 1158 } 1159 $factor = round($factor * 1000.0) / 1000.0; // To avoid some wierd rounding errors (Moodle) 1160 1161 $max = $factor * @ceil($max / $factor); 1162 $min = $factor * @floor($min / $factor); 1163 1164 //print "max=$max, min=$min<br />"; 1165 1166 return array('min' => $min, 'max' => $max); 1167 } 1168 1169 function graph() { 1170 if (func_num_args() == 2) { 1171 $this->parameter['width'] = func_get_arg(0); 1172 $this->parameter['height'] = func_get_arg(1); 1173 } 1174 //$this->boundaryBox = array( 1175 $this->calculated['boundary_box'] = array( 1176 'left' => 0, 1177 'top' => 0, 1178 'right' => $this->parameter['width'] - 1, 1179 'bottom' => $this->parameter['height'] - 1); 1180 1181 $this->init_colours(); 1182 1183 //ImageColorTransparent($this->image, $this->colour['white']); // colour for transparency 1184 } 1185 1186 function print_TTF($message) { 1187 $points = $message['points']; 1188 $angle = $message['angle']; 1189 $text = $message['text']; 1190 $colour = $this->colour[$message['colour']]; 1191 $font = $this->parameter['path_to_fonts'].$message['font']; 1192 1193 $x = $message['boundary_box']['x']; 1194 $y = $message['boundary_box']['y']; 1195 $offsetX = $message['boundary_box']['offsetX']; 1196 $offsetY = $message['boundary_box']['offsetY']; 1197 $height = $message['boundary_box']['height']; 1198 $width = $message['boundary_box']['width']; 1199 $reference = $message['boundary_box']['reference']; 1200 1201 switch ($reference) { 1202 case 'top-left': 1203 case 'left-top': 1204 $y += $height - $offsetY; 1205 //$y += $offsetY; 1206 $x += $offsetX; 1207 break; 1208 case 'left-center': 1209 $y += ($height / 2) - $offsetY; 1210 $x += $offsetX; 1211 break; 1212 case 'left-bottom': 1213 $y -= $offsetY; 1214 $x += $offsetX; 1215 break; 1216 case 'top-center': 1217 $y += $height - $offsetY; 1218 $x -= ($width / 2) - $offsetX; 1219 break; 1220 case 'top-right': 1221 case 'right-top': 1222 $y += $height - $offsetY; 1223 $x -= $width - $offsetX; 1224 break; 1225 case 'right-center': 1226 $y += ($height / 2) - $offsetY; 1227 $x -= $width - $offsetX; 1228 break; 1229 case 'right-bottom': 1230 $y -= $offsetY; 1231 $x -= $width - $offsetX; 1232 break; 1233 case 'bottom-center': 1234 $y -= $offsetY; 1235 $x -= ($width / 2) - $offsetX; 1236 break; 1237 default: 1238 $y = 0; 1239 $x = 0; 1240 break; 1241 } 1242 // start of Moodle addition 1243 $textlib = textlib_get_instance(); 1244 $text = $textlib->utf8_to_entities($text, true, true); //does not work with hex entities! 1245 // end of Moodle addition 1246 ImageTTFText($this->image, $points, $angle, $x, $y, $colour, $font, $text); 1247 } 1248 1249 // move boundaryBox to coordinates specified 1250 function update_boundaryBox(&$boundaryBox, $coords) { 1251 $width = $boundaryBox['width']; 1252 $height = $boundaryBox['height']; 1253 $x = $coords['x']; 1254 $y = $coords['y']; 1255 $reference = $coords['reference']; 1256 switch ($reference) { 1257 case 'top-left': 1258 case 'left-top': 1259 $top = $y; 1260 $bottom = $y + $height; 1261 $left = $x; 1262 $right = $x + $width; 1263 break; 1264 case 'left-center': 1265 $top = $y - ($height / 2); 1266 $bottom = $y + ($height / 2); 1267 $left = $x; 1268 $right = $x + $width; 1269 break; 1270 case 'left-bottom': 1271 $top = $y - $height; 1272 $bottom = $y; 1273 $left = $x; 1274 $right = $x + $width; 1275 break; 1276 case 'top-center': 1277 $top = $y; 1278 $bottom = $y + $height; 1279 $left = $x - ($width / 2); 1280 $right = $x + ($width / 2); 1281 break; 1282 case 'right-top': 1283 case 'top-right': 1284 $top = $y; 1285 $bottom = $y + $height; 1286 $left = $x - $width; 1287 $right = $x; 1288 break; 1289 case 'right-center': 1290 $top = $y - ($height / 2); 1291 $bottom = $y + ($height / 2); 1292 $left = $x - $width; 1293 $right = $x; 1294 break; 1295 case 'bottom=right': 1296 case 'right-bottom': 1297 $top = $y - $height; 1298 $bottom = $y; 1299 $left = $x - $width; 1300 $right = $x; 1301 break; 1302 default: 1303 $top = 0; 1304 $bottom = $height; 1305 $left = 0; 1306 $right = $width; 1307 break; 1308 } 1309 1310 $boundaryBox = array_merge($boundaryBox, array('top' => $top, 1311 'bottom' => $bottom, 1312 'left' => $left, 1313 'right' => $right, 1314 'x' => $x, 1315 'y' => $y, 1316 'reference' => $reference)); 1317 } 1318 1319 function get_null_size() { 1320 return array('width' => 0, 1321 'height' => 0, 1322 'offsetX' => 0, 1323 'offsetY' => 0, 1324 //'fontHeight' => 0 1325 ); 1326 } 1327 1328 function get_boundaryBox($message) { 1329 $points = $message['points']; 1330 $angle = $message['angle']; 1331 $font = $this->parameter['path_to_fonts'].$message['font']; 1332 $text = $message['text']; 1333 1334 //print ('get_boundaryBox'); 1335 //expandPre($message); 1336 1337 // get font size 1338 $bounds = ImageTTFBBox($points, $angle, $font, "W"); 1339 if ($angle < 0) { 1340 $fontHeight = abs($bounds[7]-$bounds[1]); 1341 } else if ($angle > 0) { 1342 $fontHeight = abs($bounds[1]-$bounds[7]); 1343 } else { 1344 $fontHeight = abs($bounds[7]-$bounds[1]); 1345 } 1346 1347 // get boundary box and offsets for printing at an angle 1348 // start of Moodle addition 1349 $textlib = textlib_get_instance(); 1350 $text = $textlib->utf8_to_entities($text, true, true); //gd does not work with hex entities! 1351 // end of Moodle addition 1352 $bounds = ImageTTFBBox($points, $angle, $font, $text); 1353 1354 if ($angle < 0) { 1355 $width = abs($bounds[4]-$bounds[0]); 1356 $height = abs($bounds[3]-$bounds[7]); 1357 $offsetY = abs($bounds[3]-$bounds[1]); 1358 $offsetX = 0; 1359 1360 } else if ($angle > 0) { 1361 $width = abs($bounds[2]-$bounds[6]); 1362 $height = abs($bounds[1]-$bounds[5]); 1363 $offsetY = 0; 1364 $offsetX = abs($bounds[0]-$bounds[6]); 1365 1366 } else { 1367 $width = abs($bounds[4]-$bounds[6]); 1368 $height = abs($bounds[7]-$bounds[1]); 1369 $offsetY = 0; 1370 $offsetX = 0; 1371 } 1372 1373 //return values 1374 return array('width' => $width, 1375 'height' => $height, 1376 'offsetX' => $offsetX, 1377 'offsetY' => $offsetY, 1378 //'fontHeight' => $fontHeight 1379 ); 1380 } 1381 1382 function draw_rectangle($border, $colour, $type) { 1383 $colour = $this->colour[$colour]; 1384 switch ($type) { 1385 case 'fill': // fill the rectangle 1386 ImageFilledRectangle($this->image, $border['left'], $border['top'], $border['right'], $border['bottom'], $colour); 1387 break; 1388 case 'box': // all sides 1389 ImageRectangle($this->image, $border['left'], $border['top'], $border['right'], $border['bottom'], $colour); 1390 break; 1391 case 'axis': // bottom x axis and left y axis 1392 ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); 1393 ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); 1394 break; 1395 case 'y': // left y axis only 1396 case 'y-left': 1397 ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); 1398 break; 1399 case 'y-right': // right y axis only 1400 ImageLine($this->image, $border['right'], $border['top'], $border['right'], $border['bottom'], $colour); 1401 break; 1402 case 'x': // bottom x axis only 1403 ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); 1404 break; 1405 case 'u': // u shaped. bottom x axis and both left and right y axis. 1406 ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); 1407 ImageLine($this->image, $border['right'], $border['top'], $border['right'], $border['bottom'], $colour); 1408 ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); 1409 break; 1410 1411 } 1412 } 1413 1414 function init_colours() { 1415 $this->image = ImageCreate($this->parameter['width'], $this->parameter['height']); 1416 // standard colours 1417 $this->colour['white'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0xFF); // first colour is background colour. 1418 $this->colour['black'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0x00); 1419 $this->colour['maroon'] = ImageColorAllocate ($this->image, 0x80, 0x00, 0x00); 1420 $this->colour['green'] = ImageColorAllocate ($this->image, 0x00, 0x80, 0x00); 1421 $this->colour['ltgreen'] = ImageColorAllocate ($this->image, 0x52, 0xF1, 0x7F); 1422 $this->colour['ltltgreen']= ImageColorAllocate ($this->image, 0x99, 0xFF, 0x99); 1423 $this->colour['olive'] = ImageColorAllocate ($this->image, 0x80, 0x80, 0x00); 1424 $this->colour['navy'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0x80); 1425 $this->colour['purple'] = ImageColorAllocate ($this->image, 0x80, 0x00, 0x80); 1426 $this->colour['gray'] = ImageColorAllocate ($this->image, 0x80, 0x80, 0x80); 1427 $this->colour['red'] = ImageColorAllocate ($this->image, 0xFF, 0x00, 0x00); 1428 $this->colour['ltred'] = ImageColorAllocate ($this->image, 0xFF, 0x99, 0x99); 1429 $this->colour['ltltred'] = ImageColorAllocate ($this->image, 0xFF, 0xCC, 0xCC); 1430 $this->colour['orange'] = ImageColorAllocate ($this->image, 0xFF, 0x66, 0x00); 1431 $this->colour['ltorange'] = ImageColorAllocate ($this->image, 0xFF, 0x99, 0x66); 1432 $this->colour['ltltorange'] = ImageColorAllocate ($this->image, 0xFF, 0xcc, 0x99); 1433 $this->colour['lime'] = ImageColorAllocate ($this->image, 0x00, 0xFF, 0x00); 1434 $this->colour['yellow'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0x00);