|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00029 defined('MOODLE_INTERNAL') || die(); 00030 00031 /* This file contains modifications by Martin Dougiamas 00032 * as part of Moodle (http://moodle.com). Modified lines 00033 * are marked with "Moodle". 00034 */ 00035 00039 class graph { 00040 var $image; 00041 var $debug = FALSE; // be careful!! 00042 var $calculated = array(); // array of computed values for chart 00043 var $parameter = array( // input parameters 00044 'width' => 320, // default width of image 00045 'height' => 240, // default height of image 00046 'file_name' => 'none', // name of file for file to be saved as. 00047 // NOTE: no suffix required. this is determined from output_format below. 00048 'output_format' => 'PNG', // image output format. 'GIF', 'PNG', 'JPEG'. default 'PNG'. 00049 00050 'seconds_to_live' => 0, // expiry time in seconds (for HTTP header) 00051 'hours_to_live' => 0, // expiry time in hours (for HTTP header) 00052 'path_to_fonts' => 'fonts/', // path to fonts folder. don't forget *trailing* slash!! 00053 // for WINDOZE this may need to be the full path, not relative. 00054 00055 'title' => 'Graph Title', // text for graph title 00056 'title_font' => 'default.ttf', // title text font. don't forget to set 'path_to_fonts' above. 00057 'title_size' => 16, // title text point size 00058 'title_colour' => 'black', // colour for title text 00059 00060 'x_label' => '', // if this is set then this text is printed on bottom axis of graph. 00061 'y_label_left' => '', // if this is set then this text is printed on left axis of graph. 00062 'y_label_right' => '', // if this is set then this text is printed on right axis of graph. 00063 00064 'label_size' => 8, // label text point size 00065 'label_font' => 'default.ttf', // label text font. don't forget to set 'path_to_fonts' above. 00066 'label_colour' => 'gray33', // label text colour 00067 'y_label_angle' => 90, // rotation of y axis label 00068 00069 'x_label_angle' => 90, // rotation of y axis label 00070 00071 'outer_padding' => 5, // padding around outer text. i.e. title, y label, and x label. 00072 'inner_padding' => 0, // padding beteen axis text and graph. 00073 'x_inner_padding' => 5, // padding beteen axis text and graph. 00074 'y_inner_padding' => 6, // padding beteen axis text and graph. 00075 'outer_border' => 'none', // colour of border aound image, or 'none'. 00076 'inner_border' => 'black', // colour of border around actual graph, or 'none'. 00077 'inner_border_type' => 'box', // 'box' for all four sides, 'axis' for x/y axis only, 00078 // 'y' or 'y-left' for y axis only, 'y-right' for right y axis only, 00079 // 'x' for x axis only, 'u' for both left and right y axis and x axis. 00080 'outer_background' => 'none', // background colour of entire image. 00081 'inner_background' => 'none', // background colour of plot area. 00082 00083 'y_min_left' => 0, // this will be reset to minimum value if there is a value lower than this. 00084 'y_max_left' => 0, // this will be reset to maximum value if there is a value higher than this. 00085 'y_min_right' => 0, // this will be reset to minimum value if there is a value lower than this. 00086 'y_max_right' => 0, // this will be reset to maximum value if there is a value higher than this. 00087 'x_min' => 0, // only used if x axis is numeric. 00088 'x_max' => 0, // only used if x axis is numeric. 00089 00090 'y_resolution_left' => 1, // scaling for rounding of y axis max value. 00091 // if max y value is 8645 then 00092 // if y_resolution is 0, then y_max becomes 9000. 00093 // if y_resolution is 1, then y_max becomes 8700. 00094 // if y_resolution is 2, then y_max becomes 8650. 00095 // if y_resolution is 3, then y_max becomes 8645. 00096 // get it? 00097 'y_decimal_left' => 0, // number of decimal places for y_axis text. 00098 'y_resolution_right' => 2, // ... same for right hand side 00099 'y_decimal_right' => 0, // ... same for right hand side 00100 'x_resolution' => 2, // only used if x axis is numeric. 00101 'x_decimal' => 0, // only used if x axis is numeric. 00102 00103 'point_size' => 4, // default point size. use even number for diamond or triangle to get nice look. 00104 'brush_size' => 4, // default brush size for brush line. 00105 'brush_type' => 'circle', // type of brush to use to draw line. choose from the following 00106 // 'circle', 'square', 'horizontal', 'vertical', 'slash', 'backslash' 00107 'bar_size' => 0.8, // size of bar to draw. <1 bars won't touch 00108 // 1 is full width - i.e. bars will touch. 00109 // >1 means bars will overlap. 00110 'bar_spacing' => 10, // space in pixels between group of bars for each x value. 00111 'shadow_offset' => 3, // draw shadow at this offset, unless overidden by data parameter. 00112 'shadow' => 'grayCC', // 'none' or colour of shadow. 00113 'shadow_below_axis' => true, // whether to draw shadows of bars and areas below the x/zero axis. 00114 00115 00116 'x_axis_gridlines' => 'auto', // if set to a number then x axis is treated as numeric. 00117 'y_axis_gridlines' => 6, // number of gridlines on y axis. 00118 'zero_axis' => 'none', // colour to draw zero-axis, or 'none'. 00119 00120 00121 'axis_font' => 'default.ttf', // axis text font. don't forget to set 'path_to_fonts' above. 00122 'axis_size' => 8, // axis text font size in points 00123 'axis_colour' => 'gray33', // colour of axis text. 00124 'y_axis_angle' => 0, // rotation of axis text. 00125 'x_axis_angle' => 0, // rotation of axis text. 00126 00127 'y_axis_text_left' => 1, // whether to print left hand y axis text. if 0 no text, if 1 all ticks have text, 00128 'x_axis_text' => 1, // if 4 then print every 4th tick and text, etc... 00129 'y_axis_text_right' => 0, // behaviour same as above for right hand y axis. 00130 00131 'x_offset' => 0.5, // x axis tick offset from y axis as fraction of tick spacing. 00132 'y_ticks_colour' => 'black', // colour to draw y ticks, or 'none' 00133 'x_ticks_colour' => 'black', // colour to draw x ticks, or 'none' 00134 'y_grid' => 'line', // grid lines. set to 'line' or 'dash'... 00135 'x_grid' => 'line', // or if set to 'none' print nothing. 00136 'grid_colour' => 'grayEE', // default grid colour. 00137 'tick_length' => 4, // length of ticks in pixels. can be negative. i.e. outside data drawing area. 00138 00139 'legend' => 'none', // default. no legend. 00140 // otherwise: 'top-left', 'top-right', 'bottom-left', 'bottom-right', 00141 // 'outside-top', 'outside-bottom', 'outside-left', or 'outside-right'. 00142 'legend_offset' => 10, // offset in pixels from graph or outside border. 00143 'legend_padding' => 5, // padding around legend text. 00144 'legend_font' => 'default.ttf', // legend text font. don't forget to set 'path_to_fonts' above. 00145 'legend_size' => 8, // legend text point size. 00146 'legend_colour' => 'black', // legend text colour. 00147 'legend_border' => 'none', // legend border colour, or 'none'. 00148 00149 'decimal_point' => '.', // symbol for decimal separation '.' or ',' *european support. 00150 'thousand_sep' => ',', // symbol for thousand separation ',' or '' 00151 00152 ); 00153 var $y_tick_labels = null; // array of text values for y-axis tick labels 00154 var $offset_relation = null; // array of offsets for different sets of data 00155 00156 00157 // init all text - title, labels, and axis text. 00158 function init() { 00159 00161 00162 global $CFG; 00163 00170 00171 $currlang = current_language(); 00172 if (file_exists("$CFG->dataroot/lang/".$currlang."_local/fonts/default.ttf")) { 00173 $fontpath = "$CFG->dataroot/lang/".$currlang."_local/fonts/"; 00174 } else if (file_exists("$CFG->dataroot/lang/$currlang/fonts/default.ttf")) { 00175 $fontpath = "$CFG->dataroot/lang/$currlang/fonts/"; 00176 } else if (file_exists("$CFG->dirroot/lang/$currlang/fonts/default.ttf")) { 00177 $fontpath = "$CFG->dirroot/lang/$currlang/fonts/"; 00178 } else if (file_exists("$CFG->dataroot/lang/default.ttf")) { 00179 $fontpath = "$CFG->dataroot/lang/"; 00180 } else { 00181 $fontpath = "$CFG->libdir/"; 00182 } 00183 00184 $this->parameter['path_to_fonts'] = $fontpath; 00185 00187 00188 00189 00190 $this->calculated['outer_border'] = $this->calculated['boundary_box']; 00191 00192 // outer padding 00193 $this->calculated['boundary_box']['left'] += $this->parameter['outer_padding']; 00194 $this->calculated['boundary_box']['top'] += $this->parameter['outer_padding']; 00195 $this->calculated['boundary_box']['right'] -= $this->parameter['outer_padding']; 00196 $this->calculated['boundary_box']['bottom'] -= $this->parameter['outer_padding']; 00197 00198 $this->init_x_axis(); 00199 $this->init_y_axis(); 00200 $this->init_legend(); 00201 $this->init_labels(); 00202 00203 // take into account tick lengths 00204 $this->calculated['bottom_inner_padding'] = $this->parameter['x_inner_padding']; 00205 if (($this->parameter['x_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) 00206 $this->calculated['bottom_inner_padding'] -= $this->parameter['tick_length']; 00207 $this->calculated['boundary_box']['bottom'] -= $this->calculated['bottom_inner_padding']; 00208 00209 $this->calculated['left_inner_padding'] = $this->parameter['y_inner_padding']; 00210 if ($this->parameter['y_axis_text_left']) { 00211 if (($this->parameter['y_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) 00212 $this->calculated['left_inner_padding'] -= $this->parameter['tick_length']; 00213 } 00214 $this->calculated['boundary_box']['left'] += $this->calculated['left_inner_padding']; 00215 00216 $this->calculated['right_inner_padding'] = $this->parameter['y_inner_padding']; 00217 if ($this->parameter['y_axis_text_right']) { 00218 if (($this->parameter['y_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) 00219 $this->calculated['right_inner_padding'] -= $this->parameter['tick_length']; 00220 } 00221 $this->calculated['boundary_box']['right'] -= $this->calculated['right_inner_padding']; 00222 00223 // boundaryBox now has coords for plotting area. 00224 $this->calculated['inner_border'] = $this->calculated['boundary_box']; 00225 00226 $this->init_data(); 00227 $this->init_x_ticks(); 00228 $this->init_y_ticks(); 00229 } 00230 00231 function draw_text() { 00232 $colour = $this->parameter['outer_background']; 00233 if ($colour != 'none') $this->draw_rectangle($this->calculated['outer_border'], $colour, 'fill'); // graph background 00234 00235 // draw border around image 00236 $colour = $this->parameter['outer_border']; 00237 if ($colour != 'none') $this->draw_rectangle($this->calculated['outer_border'], $colour, 'box'); // graph border 00238 00239 $this->draw_title(); 00240 $this->draw_x_label(); 00241 $this->draw_y_label_left(); 00242 $this->draw_y_label_right(); 00243 $this->draw_x_axis(); 00244 $this->draw_y_axis(); 00245 if ($this->calculated['y_axis_left']['has_data']) $this->draw_zero_axis_left(); // either draw zero axis on left 00246 else if ($this->calculated['y_axis_right']['has_data']) $this->draw_zero_axis_right(); // ... or right. 00247 $this->draw_legend(); 00248 00249 // draw border around plot area 00250 $colour = $this->parameter['inner_background']; 00251 if ($colour != 'none') $this->draw_rectangle($this->calculated['inner_border'], $colour, 'fill'); // graph background 00252 00253 // draw border around image 00254 $colour = $this->parameter['inner_border']; 00255 if ($colour != 'none') $this->draw_rectangle($this->calculated['inner_border'], $colour, $this->parameter['inner_border_type']); // graph border 00256 } 00257 00258 function draw_stack() { 00259 $this->init(); 00260 $this->draw_text(); 00261 00262 $yOrder = $this->y_order; // save y_order data. 00263 // iterate over each data set. order is very important if you want to see data correctly. remember shadows!! 00264 foreach ($yOrder as $set) { 00265 $this->y_order = array($set); 00266 $this->init_data(); 00267 $this->draw_data(); 00268 } 00269 $this->y_order = $yOrder; // revert y_order data. 00270 00271 $this->output(); 00272 } 00273 00274 function draw() { 00275 $this->init(); 00276 $this->draw_text(); 00277 $this->draw_data(); 00278 $this->output(); 00279 } 00280 00281 // draw a data set 00282 function draw_set($order, $set, $offset) { 00283 if ($offset) @$this->init_variable($colour, $this->y_format[$set]['shadow'], $this->parameter['shadow']); 00284 else $colour = $this->y_format[$set]['colour']; 00285 @$this->init_variable($point, $this->y_format[$set]['point'], 'none'); 00286 @$this->init_variable($pointSize, $this->y_format[$set]['point_size'], $this->parameter['point_size']); 00287 @$this->init_variable($line, $this->y_format[$set]['line'], 'none'); 00288 @$this->init_variable($brushType, $this->y_format[$set]['brush_type'], $this->parameter['brush_type']); 00289 @$this->init_variable($brushSize, $this->y_format[$set]['brush_size'], $this->parameter['brush_size']); 00290 @$this->init_variable($bar, $this->y_format[$set]['bar'], 'none'); 00291 @$this->init_variable($barSize, $this->y_format[$set]['bar_size'], $this->parameter['bar_size']); 00292 @$this->init_variable($area, $this->y_format[$set]['area'], 'none'); 00293 00294 $lastX = 0; 00295 $lastY = 'none'; 00296 $fromX = 0; 00297 $fromY = 'none'; 00298 00299 //print "set $set<br />"; 00300 //expand_pre($this->calculated['y_plot']); 00301 00302 foreach ($this->x_data as $index => $x) { 00303 //print "index $index<br />"; 00304 $thisY = $this->calculated['y_plot'][$set][$index]; 00305 $thisX = $this->calculated['x_plot'][$index]; 00306 00307 //print "$thisX, $thisY <br />"; 00308 00309 if (($bar!='none') && (string)$thisY != 'none') { 00310 if ($relatedset = $this->offset_relation[$set]) { // Moodle 00311 $yoffset = $this->calculated['y_plot'][$relatedset][$index]; // Moodle 00312 } else { // Moodle 00313 $yoffset = 0; // Moodle 00314 } // Moodle 00315 //$this->bar($thisX, $thisY, $bar, $barSize, $colour, $offset, $set); // Moodle 00316 $this->bar($thisX, $thisY, $bar, $barSize, $colour, $offset, $set, $yoffset); // Moodle 00317 } 00318 00319 if (($area!='none') && (((string)$lastY != 'none') && ((string)$thisY != 'none'))) 00320 $this->area($lastX, $lastY, $thisX, $thisY, $area, $colour, $offset); 00321 00322 if (($point!='none') && (string)$thisY != 'none') $this->plot($thisX, $thisY, $point, $pointSize, $colour, $offset); 00323 00324 if (($line!='none') && ((string)$thisY != 'none')) { 00325 if ((string)$fromY != 'none') 00326 $this->line($fromX, $fromY, $thisX, $thisY, $line, $brushType, $brushSize, $colour, $offset); 00327 00328 $fromY = $thisY; // start next line from here 00329 $fromX = $thisX; // ... 00330 } else { 00331 $fromY = 'none'; 00332 $fromX = 'none'; 00333 } 00334 00335 $lastX = $thisX; 00336 $lastY = $thisY; 00337 } 00338 } 00339 00340 function draw_data() { 00341 // cycle thru y data to be plotted 00342 // first check for drop shadows... 00343 foreach ($this->y_order as $order => $set) { 00344 @$this->init_variable($offset, $this->y_format[$set]['shadow_offset'], $this->parameter['shadow_offset']); 00345 @$this->init_variable($colour, $this->y_format[$set]['shadow'], $this->parameter['shadow']); 00346 if ($colour != 'none') $this->draw_set($order, $set, $offset); 00347 00348 } 00349 00350 // then draw data 00351 foreach ($this->y_order as $order => $set) { 00352 $this->draw_set($order, $set, 0); 00353 } 00354 } 00355 00356 function draw_legend() { 00357 $position = $this->parameter['legend']; 00358 if ($position == 'none') return; // abort if no border 00359 00360 $borderColour = $this->parameter['legend_border']; 00361 $offset = $this->parameter['legend_offset']; 00362 $padding = $this->parameter['legend_padding']; 00363 $height = $this->calculated['legend']['boundary_box_all']['height']; 00364 $width = $this->calculated['legend']['boundary_box_all']['width']; 00365 $graphTop = $this->calculated['boundary_box']['top']; 00366 $graphBottom = $this->calculated['boundary_box']['bottom']; 00367 $graphLeft = $this->calculated['boundary_box']['left']; 00368 $graphRight = $this->calculated['boundary_box']['right']; 00369 $outsideRight = $this->calculated['outer_border']['right']; 00370 $outsideBottom = $this->calculated['outer_border']['bottom']; 00371 switch ($position) { 00372 case 'top-left': 00373 $top = $graphTop + $offset; 00374 $bottom = $graphTop + $height + $offset; 00375 $left = $graphLeft + $offset; 00376 $right = $graphLeft + $width + $offset; 00377 00378 break; 00379 case 'top-right': 00380 $top = $graphTop + $offset; 00381 $bottom = $graphTop + $height + $offset; 00382 $left = $graphRight - $width - $offset; 00383 $right = $graphRight - $offset; 00384 00385 break; 00386 case 'bottom-left': 00387 $top = $graphBottom - $height - $offset; 00388 $bottom = $graphBottom - $offset; 00389 $left = $graphLeft + $offset; 00390 $right = $graphLeft + $width + $offset; 00391 00392 break; 00393 case 'bottom-right': 00394 $top = $graphBottom - $height - $offset; 00395 $bottom = $graphBottom - $offset; 00396 $left = $graphRight - $width - $offset; 00397 $right = $graphRight - $offset; 00398 break; 00399 00400 case 'outside-top' : 00401 $top = $graphTop; 00402 $bottom = $graphTop + $height; 00403 $left = $outsideRight - $width - $offset; 00404 $right = $outsideRight - $offset; 00405 break; 00406 00407 case 'outside-bottom' : 00408 $top = $graphBottom - $height; 00409 $bottom = $graphBottom; 00410 $left = $outsideRight - $width - $offset; 00411 $right = $outsideRight - $offset; 00412 break; 00413 00414 case 'outside-left' : 00415 $top = $outsideBottom - $height - $offset; 00416 $bottom = $outsideBottom - $offset; 00417 $left = $graphLeft; 00418 $right = $graphLeft + $width; 00419 break; 00420 00421 case 'outside-right' : 00422 $top = $outsideBottom - $height - $offset; 00423 $bottom = $outsideBottom - $offset; 00424 $left = $graphRight - $width; 00425 $right = $graphRight; 00426 break; 00427 default: // default is top left. no particular reason. 00428 $top = $this->calculated['boundary_box']['top']; 00429 $bottom = $this->calculated['boundary_box']['top'] + $this->calculated['legend']['boundary_box_all']['height']; 00430 $left = $this->calculated['boundary_box']['left']; 00431 $right = $this->calculated['boundary_box']['right'] + $this->calculated['legend']['boundary_box_all']['width']; 00432 00433 } 00434 // legend border 00435 if($borderColour!='none') $this->draw_rectangle(array('top' => $top, 00436 'left' => $left, 00437 'bottom' => $bottom, 00438 'right' => $right), $this->parameter['legend_border'], 'box'); 00439 00440 // legend text 00441 $legendText = array('points' => $this->parameter['legend_size'], 00442 'angle' => 0, 00443 'font' => $this->parameter['legend_font'], 00444 'colour' => $this->parameter['legend_colour']); 00445 00446 $box = $this->calculated['legend']['boundary_box_max']['height']; // use max height for legend square size. 00447 $x = $left + $padding; 00448 $x_text = $x + $box * 2; 00449 $y = $top + $padding; 00450 00451 foreach ($this->y_order as $set) { 00452 $legendText['text'] = $this->calculated['legend']['text'][$set]; 00453 if ($legendText['text'] != 'none') { 00454 // if text exists then draw box and text 00455 $boxColour = $this->colour[$this->y_format[$set]['colour']]; 00456 00457 // draw box 00458 ImageFilledRectangle($this->image, $x, $y, $x + $box, $y + $box, $boxColour); 00459 00460 // draw text 00461 $coords = array('x' => $x + $box * 2, 'y' => $y, 'reference' => 'top-left'); 00462 $legendText['boundary_box'] = $this->calculated['legend']['boundary_box'][$set]; 00463 $this->update_boundaryBox($legendText['boundary_box'], $coords); 00464 $this->print_TTF($legendText); 00465 $y += $padding + $box; 00466 } 00467 } 00468 00469 } 00470 00471 function draw_y_label_right() { 00472 if (!$this->parameter['y_label_right']) return; 00473 $x = $this->calculated['boundary_box']['right'] + $this->parameter['y_inner_padding']; 00474 if ($this->parameter['y_axis_text_right']) $x += $this->calculated['y_axis_right']['boundary_box_max']['width'] 00475 + $this->calculated['right_inner_padding']; 00476 $y = ($this->calculated['boundary_box']['bottom'] + $this->calculated['boundary_box']['top']) / 2; 00477 00478 $label = $this->calculated['y_label_right']; 00479 $coords = array('x' => $x, 'y' => $y, 'reference' => 'left-center'); 00480 $this->update_boundaryBox($label['boundary_box'], $coords); 00481 $this->print_TTF($label); 00482 } 00483 00484 00485 function draw_y_label_left() { 00486 if (!$this->parameter['y_label_left']) return; 00487 $x = $this->calculated['boundary_box']['left'] - $this->parameter['y_inner_padding']; 00488 if ($this->parameter['y_axis_text_left']) $x -= $this->calculated['y_axis_left']['boundary_box_max']['width'] 00489 + $this->calculated['left_inner_padding']; 00490 $y = ($this->calculated['boundary_box']['bottom'] + $this->calculated['boundary_box']['top']) / 2; 00491 00492 $label = $this->calculated['y_label_left']; 00493 $coords = array('x' => $x, 'y' => $y, 'reference' => 'right-center'); 00494 $this->update_boundaryBox($label['boundary_box'], $coords); 00495 $this->print_TTF($label); 00496 } 00497 00498 function draw_title() { 00499 if (!$this->parameter['title']) return; 00500 //$y = $this->calculated['outside_border']['top'] + $this->parameter['outer_padding']; 00501 $y = $this->calculated['boundary_box']['top'] - $this->parameter['outer_padding']; 00502 $x = ($this->calculated['boundary_box']['right'] + $this->calculated['boundary_box']['left']) / 2; 00503 $label = $this->calculated['title']; 00504 $coords = array('x' => $x, 'y' => $y, 'reference' => 'bottom-center'); 00505 $this->update_boundaryBox($label['boundary_box'], $coords); 00506 $this->print_TTF($label); 00507 } 00508 00509 function draw_x_label() { 00510 if (!$this->parameter['x_label']) return; 00511 $y = $this->calculated['boundary_box']['bottom'] + $this->parameter['x_inner_padding']; 00512 if ($this->parameter['x_axis_text']) $y += $this->calculated['x_axis']['boundary_box_max']['height'] 00513 + $this->calculated['bottom_inner_padding']; 00514 $x = ($this->calculated['boundary_box']['right'] + $this->calculated['boundary_box']['left']) / 2; 00515 $label = $this->calculated['x_label']; 00516 $coords = array('x' => $x, 'y' => $y, 'reference' => 'top-center'); 00517 $this->update_boundaryBox($label['boundary_box'], $coords); 00518 $this->print_TTF($label); 00519 } 00520 00521 function draw_zero_axis_left() { 00522 $colour = $this->parameter['zero_axis']; 00523 if ($colour == 'none') return; 00524 // draw zero axis on left hand side 00525 $this->calculated['zero_axis'] = round($this->calculated['boundary_box']['top'] + ($this->calculated['y_axis_left']['max'] * $this->calculated['y_axis_left']['factor'])); 00526 ImageLine($this->image, $this->calculated['boundary_box']['left'], $this->calculated['zero_axis'], $this->calculated['boundary_box']['right'], $this->calculated['zero_axis'], $this->colour[$colour]); 00527 } 00528 00529 function draw_zero_axis_right() { 00530 $colour = $this->parameter['zero_axis']; 00531 if ($colour == 'none') return; 00532 // draw zero axis on right hand side 00533 $this->calculated['zero_axis'] = round($this->calculated['boundary_box']['top'] + ($this->calculated['y_axis_right']['max'] * $this->calculated['y_axis_right']['factor'])); 00534 ImageLine($this->image, $this->calculated['boundary_box']['left'], $this->calculated['zero_axis'], $this->calculated['boundary_box']['right'], $this->calculated['zero_axis'], $this->colour[$colour]); 00535 } 00536 00537 function draw_x_axis() { 00538 $gridColour = $this->colour[$this->parameter['grid_colour']]; 00539 $tickColour = $this->colour[$this->parameter['x_ticks_colour']]; 00540 $axis_colour = $this->parameter['axis_colour']; 00541 $xGrid = $this->parameter['x_grid']; 00542 $gridTop = $this->calculated['boundary_box']['top']; 00543 $gridBottom = $this->calculated['boundary_box']['bottom']; 00544 00545 if ($this->parameter['tick_length'] >= 0) { 00546 $tickTop = $this->calculated['boundary_box']['bottom'] - $this->parameter['tick_length']; 00547 $tickBottom = $this->calculated['boundary_box']['bottom']; 00548 $textBottom = $tickBottom + $this->calculated['bottom_inner_padding']; 00549 } else { 00550 $tickTop = $this->calculated['boundary_box']['bottom']; 00551 $tickBottom = $this->calculated['boundary_box']['bottom'] - $this->parameter['tick_length']; 00552 $textBottom = $tickBottom + $this->calculated['bottom_inner_padding']; 00553 } 00554 00555 $axis_font = $this->parameter['axis_font']; 00556 $axis_size = $this->parameter['axis_size']; 00557 $axis_angle = $this->parameter['x_axis_angle']; 00558 00559 if ($axis_angle == 0) $reference = 'top-center'; 00560 if ($axis_angle > 0) $reference = 'top-right'; 00561 if ($axis_angle < 0) $reference = 'top-left'; 00562 if ($axis_angle == 90) $reference = 'top-center'; 00563 00564 //generic tag information. applies to all axis text. 00565 $axisTag = array('points' => $axis_size, 'angle' => $axis_angle, 'font' => $axis_font, 'colour' => $axis_colour); 00566 00567 foreach ($this->calculated['x_axis']['tick_x'] as $set => $tickX) { 00568 // draw x grid if colour specified 00569 if ($xGrid != 'none') { 00570 switch ($xGrid) { 00571 case 'line': 00572 ImageLine($this->image, round($tickX), round($gridTop), round($tickX), round($gridBottom), $gridColour); 00573 break; 00574 case 'dash': 00575 ImageDashedLine($this->image, round($tickX), round($gridTop), round($tickX), round($gridBottom), $gridColour); 00576 break; 00577 } 00578 } 00579 00580 if ($this->parameter['x_axis_text'] && !($set % $this->parameter['x_axis_text'])) { // test if tick should be displayed 00581 // draw tick 00582 if ($tickColour != 'none') 00583 ImageLine($this->image, round($tickX), round($tickTop), round($tickX), round($tickBottom), $tickColour); 00584 00585 // draw axis text 00586 $coords = array('x' => $tickX, 'y' => $textBottom, 'reference' => $reference); 00587 $axisTag['text'] = $this->calculated['x_axis']['text'][$set]; 00588 $axisTag['boundary_box'] = $this->calculated['x_axis']['boundary_box'][$set]; 00589 $this->update_boundaryBox($axisTag['boundary_box'], $coords); 00590 $this->print_TTF($axisTag); 00591 } 00592 } 00593 } 00594 00595 function draw_y_axis() { 00596 $gridColour = $this->colour[$this->parameter['grid_colour']]; 00597 $tickColour = $this->colour[$this->parameter['y_ticks_colour']]; 00598 $axis_colour = $this->parameter['axis_colour']; 00599 $yGrid = $this->parameter['y_grid']; 00600 $gridLeft = $this->calculated['boundary_box']['left']; 00601 $gridRight = $this->calculated['boundary_box']['right']; 00602 00603 // axis font information 00604 $axis_font = $this->parameter['axis_font']; 00605 $axis_size = $this->parameter['axis_size']; 00606 $axis_angle = $this->parameter['y_axis_angle']; 00607 $axisTag = array('points' => $axis_size, 'angle' => $axis_angle, 'font' => $axis_font, 'colour' => $axis_colour); 00608 00609 00610 if ($this->calculated['y_axis_left']['has_data']) { 00611 // LEFT HAND SIDE 00612 // left and right coords for ticks 00613 if ($this->parameter['tick_length'] >= 0) { 00614 $tickLeft = $this->calculated['boundary_box']['left']; 00615 $tickRight = $this->calculated['boundary_box']['left'] + $this->parameter['tick_length']; 00616 } else { 00617 $tickLeft = $this->calculated['boundary_box']['left'] + $this->parameter['tick_length']; 00618 $tickRight = $this->calculated['boundary_box']['left']; 00619 } 00620 $textRight = $tickLeft - $this->calculated['left_inner_padding']; 00621 00622 if ($axis_angle == 0) $reference = 'right-center'; 00623 if ($axis_angle > 0) $reference = 'right-top'; 00624 if ($axis_angle < 0) $reference = 'right-bottom'; 00625 if ($axis_angle == 90) $reference = 'right-center'; 00626 00627 foreach ($this->calculated['y_axis']['tick_y'] as $set => $tickY) { 00628 // draw y grid if colour specified 00629 if ($yGrid != 'none') { 00630 switch ($yGrid) { 00631 case 'line': 00632 ImageLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 00633 break; 00634 case 'dash': 00635 ImageDashedLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 00636 break; 00637 } 00638 } 00639 00640 // y axis text 00641 if ($this->parameter['y_axis_text_left'] && !($set % $this->parameter['y_axis_text_left'])) { // test if tick should be displayed 00642 // draw tick 00643 if ($tickColour != 'none') 00644 ImageLine($this->image, round($tickLeft), round($tickY), round($tickRight), round($tickY), $tickColour); 00645 00646 // draw axis text... 00647 $coords = array('x' => $textRight, 'y' => $tickY, 'reference' => $reference); 00648 $axisTag['text'] = $this->calculated['y_axis_left']['text'][$set]; 00649 $axisTag['boundary_box'] = $this->calculated['y_axis_left']['boundary_box'][$set]; 00650 $this->update_boundaryBox($axisTag['boundary_box'], $coords); 00651 $this->print_TTF($axisTag); 00652 } 00653 } 00654 } 00655 00656 if ($this->calculated['y_axis_right']['has_data']) { 00657 // RIGHT HAND SIDE 00658 // left and right coords for ticks 00659 if ($this->parameter['tick_length'] >= 0) { 00660 $tickLeft = $this->calculated['boundary_box']['right'] - $this->parameter['tick_length']; 00661 $tickRight = $this->calculated['boundary_box']['right']; 00662 } else { 00663 $tickLeft = $this->calculated['boundary_box']['right']; 00664 $tickRight = $this->calculated['boundary_box']['right'] - $this->parameter['tick_length']; 00665 } 00666 $textLeft = $tickRight+ $this->calculated['left_inner_padding']; 00667 00668 if ($axis_angle == 0) $reference = 'left-center'; 00669 if ($axis_angle > 0) $reference = 'left-bottom'; 00670 if ($axis_angle < 0) $reference = 'left-top'; 00671 if ($axis_angle == 90) $reference = 'left-center'; 00672 00673 foreach ($this->calculated['y_axis']['tick_y'] as $set => $tickY) { 00674 if (!$this->calculated['y_axis_left']['has_data'] && $yGrid != 'none') { // draw grid if not drawn already (above) 00675 switch ($yGrid) { 00676 case 'line': 00677 ImageLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 00678 break; 00679 case 'dash': 00680 ImageDashedLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); 00681 break; 00682 } 00683 } 00684 00685 if ($this->parameter['y_axis_text_right'] && !($set % $this->parameter['y_axis_text_right'])) { // test if tick should be displayed 00686 // draw tick 00687 if ($tickColour != 'none') 00688 ImageLine($this->image, round($tickLeft), round($tickY), round($tickRight), round($tickY), $tickColour); 00689 00690 // draw axis text... 00691 $coords = array('x' => $textLeft, 'y' => $tickY, 'reference' => $reference); 00692 $axisTag['text'] = $this->calculated['y_axis_right']['text'][$set]; 00693 $axisTag['boundary_box'] = $this->calculated['y_axis_left']['boundary_box'][$set]; 00694 $this->update_boundaryBox($axisTag['boundary_box'], $coords); 00695 $this->print_TTF($axisTag); 00696 } 00697 } 00698 } 00699 } 00700 00701 function init_data() { 00702 $this->calculated['y_plot'] = array(); // array to hold pixel plotting coords for y axis 00703 $height = $this->calculated['boundary_box']['bottom'] - $this->calculated['boundary_box']['top']; 00704 $width = $this->calculated['boundary_box']['right'] - $this->calculated['boundary_box']['left']; 00705 00706 // calculate pixel steps between axis ticks. 00707 $this->calculated['y_axis']['step'] = $height / ($this->parameter['y_axis_gridlines'] - 1); 00708 00709 // calculate x ticks spacing taking into account x offset for ticks. 00710 $extraTick = 2 * $this->parameter['x_offset']; // extra tick to account for padding 00711 $numTicks = $this->calculated['x_axis']['num_ticks'] - 1; // number of x ticks 00712 00713 // Hack by rodger to avoid division by zero, see bug 1231 00714 if ($numTicks==0) $numTicks=1; 00715 00716 $this->calculated['x_axis']['step'] = $width / ($numTicks + $extraTick); 00717 $widthPlot = $width - ($this->calculated['x_axis']['step'] * $extraTick); 00718 $this->calculated['x_axis']['step'] = $widthPlot / $numTicks; 00719 00720 //calculate factor for transforming x,y physical coords to logical coords for right hand y_axis. 00721 $y_range = $this->calculated['y_axis_right']['max'] - $this->calculated['y_axis_right']['min']; 00722 $y_range = ($y_range ? $y_range : 1); 00723 $this->calculated['y_axis_right']['factor'] = $height / $y_range; 00724 00725 //calculate factor for transforming x,y physical coords to logical coords for left hand axis. 00726 $yRange = $this->calculated['y_axis_left']['max'] - $this->calculated['y_axis_left']['min']; 00727 $yRange = ($yRange ? $yRange : 1); 00728 $this->calculated['y_axis_left']['factor'] = $height / $yRange; 00729 if ($this->parameter['x_axis_gridlines'] != 'auto') { 00730 $xRange = $this->calculated['x_axis']['max'] - $this->calculated['x_axis']['min']; 00731 $xRange = ($xRange ? $xRange : 1); 00732 $this->calculated['x_axis']['factor'] = $widthPlot / $xRange; 00733 } 00734 00735 //expand_pre($this->calculated['boundary_box']); 00736 // cycle thru all data sets... 00737 $this->calculated['num_bars'] = 0; 00738 foreach ($this->y_order as $order => $set) { 00739 // determine how many bars there are 00740 if (isset($this->y_format[$set]['bar']) && ($this->y_format[$set]['bar'] != 'none')) { 00741 $this->calculated['bar_offset_index'][$set] = $this->calculated['num_bars']; // index to relate bar with data set. 00742 $this->calculated['num_bars']++; 00743 } 00744 00745 // calculate y coords for plotting data 00746 foreach ($this->x_data as $index => $x) { 00747 $this->calculated['y_plot'][$set][$index] = $this->y_data[$set][$index]; 00748 00749 if ((string)$this->y_data[$set][$index] != 'none') { 00750 00751 if (isset($this->y_format[$set]['y_axis']) && $this->y_format[$set]['y_axis'] == 'right') { 00752 $this->calculated['y_plot'][$set][$index] = 00753 round(($this->y_data[$set][$index] - $this->calculated['y_axis_right']['min']) 00754 * $this->calculated['y_axis_right']['factor']); 00755 } else { 00756 //print "$set $index<br />"; 00757 $this->calculated['y_plot'][$set][$index] = 00758 round(($this->y_data[$set][$index] - $this->calculated['y_axis_left']['min']) 00759 * $this->calculated['y_axis_left']['factor']); 00760 } 00761 00762 } 00763 } 00764 } 00765 //print "factor ".$this->calculated['x_axis']['factor']."<br />"; 00766 //expand_pre($this->calculated['x_plot']); 00767 00768 // calculate bar parameters if bars are to be drawn. 00769 if ($this->calculated['num_bars']) { 00770 $xStep = $this->calculated['x_axis']['step']; 00771 $totalWidth = $this->calculated['x_axis']['step'] - $this->parameter['bar_spacing']; 00772 $barWidth = $totalWidth / $this->calculated['num_bars']; 00773 00774 $barX = ($barWidth - $totalWidth) / 2; // starting x offset 00775 for ($i=0; $i < $this->calculated['num_bars']; $i++) { 00776 $this->calculated['bar_offset_x'][$i] = $barX; 00777 $barX += $barWidth; // add width of bar to x offset. 00778 } 00779 $this->calculated['bar_width'] = $barWidth; 00780 } 00781 00782 00783 } 00784 00785 function init_x_ticks() { 00786 // get coords for x axis ticks and data plots 00787 //$xGrid = $this->parameter['x_grid']; 00788 $xStep = $this->calculated['x_axis']['step']; 00789 $ticksOffset = $this->parameter['x_offset']; // where to start drawing ticks relative to y axis. 00790 $gridLeft = $this->calculated['boundary_box']['left'] + ($xStep * $ticksOffset); // grid x start 00791 $tickX = $gridLeft; // tick x coord 00792 00793 foreach ($this->calculated['x_axis']['text'] as $set => $value) { 00794 //print "index: $set<br />"; 00795 // x tick value 00796 $this->calculated['x_axis']['tick_x'][$set] = $tickX; 00797 // if num ticks is auto then x plot value is same as x tick 00798 if ($this->parameter['x_axis_gridlines'] == 'auto') $this->calculated['x_plot'][$set] = round($tickX); 00799 //print $this->calculated['x_plot'][$set].'<br />'; 00800 $tickX += $xStep; 00801 } 00802 00803 //print "xStep: $xStep <br />"; 00804 // if numeric x axis then calculate x coords for each data point. this is seperate from x ticks. 00805 $gridX = $gridLeft; 00806 if (empty($this->calculated['x_axis']['factor'])) { 00807 $this->calculated['x_axis']['factor'] = 0; 00808 } 00809 if (empty($this->calculated['x_axis']['min'])) { 00810 $this->calculated['x_axis']['min'] = 0; 00811 } 00812 $factor = $this->calculated['x_axis']['factor']; 00813 $min = $this->calculated['x_axis']['min']; 00814 00815 if ($this->parameter['x_axis_gridlines'] != 'auto') { 00816 foreach ($this->x_data as $index => $x) { 00817 //print "index: $index, x: $x<br />"; 00818 $offset = $x - $this->calculated['x_axis']['min']; 00819 00820 //$gridX = ($offset * $this->calculated['x_axis']['factor']); 00821 //print "offset: $offset <br />"; 00822 //$this->calculated['x_plot'][$set] = $gridLeft + ($offset * $this->calculated['x_axis']['factor']); 00823 00824 $this->calculated['x_plot'][$index] = $gridLeft + ($x - $min) * $factor; 00825 00826 //print $this->calculated['x_plot'][$set].'<br />'; 00827 } 00828 } 00829 //expand_pre($this->calculated['boundary_box']); 00830 //print "factor ".$this->calculated['x_axis']['factor']."<br />"; 00831 //expand_pre($this->calculated['x_plot']); 00832 } 00833 00834 function init_y_ticks() { 00835 // get coords for y axis ticks 00836 00837 $yStep = $this->calculated['y_axis']['step']; 00838 $gridBottom = $this->calculated['boundary_box']['bottom']; 00839 $tickY = $gridBottom; // tick y coord 00840 00841 for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { 00842 $this->calculated['y_axis']['tick_y'][$i] = $tickY; 00843 $tickY -= $yStep; 00844 } 00845 00846 } 00847 00848 function init_labels() { 00849 if ($this->parameter['title']) { 00850 $size = $this->get_boundaryBox( 00851 array('points' => $this->parameter['title_size'], 00852 'angle' => 0, 00853 'font' => $this->parameter['title_font'], 00854 'text' => $this->parameter['title'])); 00855 $this->calculated['title']['boundary_box'] = $size; 00856 $this->calculated['title']['text'] = $this->parameter['title']; 00857 $this->calculated['title']['font'] = $this->parameter['title_font']; 00858 $this->calculated['title']['points'] = $this->parameter['title_size']; 00859 $this->calculated['title']['colour'] = $this->parameter['title_colour']; 00860 $this->calculated['title']['angle'] = 0; 00861 00862 $this->calculated['boundary_box']['top'] += $size['height'] + $this->parameter['outer_padding']; 00863 //$this->calculated['boundary_box']['top'] += $size['height']; 00864 00865 } else $this->calculated['title']['boundary_box'] = $this->get_null_size(); 00866 00867 if ($this->parameter['y_label_left']) { 00868 $this->calculated['y_label_left']['text'] = $this->parameter['y_label_left']; 00869 $this->calculated['y_label_left']['angle'] = $this->parameter['y_label_angle']; 00870 $this->calculated['y_label_left']['font'] = $this->parameter['label_font']; 00871 $this->calculated['y_label_left']['points'] = $this->parameter['label_size']; 00872 $this->calculated['y_label_left']['colour'] = $this->parameter['label_colour']; 00873 00874 $size = $this->get_boundaryBox($this->calculated['y_label_left']); 00875 $this->calculated['y_label_left']['boundary_box'] = $size; 00876 //$this->calculated['boundary_box']['left'] += $size['width'] + $this->parameter['inner_padding']; 00877 $this->calculated['boundary_box']['left'] += $size['width']; 00878 00879 } else $this->calculated['y_label_left']['boundary_box'] = $this->get_null_size(); 00880 00881 if ($this->parameter['y_label_right']) { 00882 $this->calculated['y_label_right']['text'] = $this->parameter['y_label_right']; 00883 $this->calculated['y_label_right']['angle'] = $this->parameter['y_label_angle']; 00884 $this->calculated['y_label_right']['font'] = $this->parameter['label_font']; 00885 $this->calculated['y_label_right']['points'] = $this->parameter['label_size']; 00886 $this->calculated['y_label_right']['colour'] = $this->parameter['label_colour']; 00887 00888 $size = $this->get_boundaryBox($this->calculated['y_label_right']); 00889 $this->calculated['y_label_right']['boundary_box'] = $size; 00890 //$this->calculated['boundary_box']['right'] -= $size['width'] + $this->parameter['inner_padding']; 00891 $this->calculated['boundary_box']['right'] -= $size['width']; 00892 00893 } else $this->calculated['y_label_right']['boundary_box'] = $this->get_null_size(); 00894 00895 if ($this->parameter['x_label']) { 00896 $this->calculated['x_label']['text'] = $this->parameter['x_label']; 00897 $this->calculated['x_label']['angle'] = $this->parameter['x_label_angle']; 00898 $this->calculated['x_label']['font'] = $this->parameter['label_font']; 00899 $this->calculated['x_label']['points'] = $this->parameter['label_size']; 00900 $this->calculated['x_label']['colour'] = $this->parameter['label_colour']; 00901 00902 $size = $this->get_boundaryBox($this->calculated['x_label']); 00903 $this->calculated['x_label']['boundary_box'] = $size; 00904 //$this->calculated['boundary_box']['bottom'] -= $size['height'] + $this->parameter['inner_padding']; 00905 $this->calculated['boundary_box']['bottom'] -= $size['height']; 00906 00907 } else $this->calculated['x_label']['boundary_box'] = $this->get_null_size(); 00908 00909 } 00910 00911 00912 function init_legend() { 00913 $this->calculated['legend'] = array(); // array to hold calculated values for legend. 00914 //$this->calculated['legend']['boundary_box_max'] = array('height' => 0, 'width' => 0); 00915 $this->calculated['legend']['boundary_box_max'] = $this->get_null_size(); 00916 if ($this->parameter['legend'] == 'none') return; 00917 00918 $position = $this->parameter['legend']; 00919 $numSets = 0; // number of data sets with legends. 00920 $sumTextHeight = 0; // total of height of all legend text items. 00921 $width = 0; 00922 $height = 0; 00923 00924 foreach ($this->y_order as $set) { 00925 $text = isset($this->y_format[$set]['legend']) ? $this->y_format[$set]['legend'] : 'none'; 00926 $size = $this->get_boundaryBox( 00927 array('points' => $this->parameter['legend_size'], 00928 'angle' => 0, 00929 'font' => $this->parameter['legend_font'], 00930 'text' => $text)); 00931 00932 $this->calculated['legend']['boundary_box'][$set] = $size; 00933 $this->calculated['legend']['text'][$set] = $text; 00934 //$this->calculated['legend']['font'][$set] = $this->parameter['legend_font']; 00935 //$this->calculated['legend']['points'][$set] = $this->parameter['legend_size']; 00936 //$this->calculated['legend']['angle'][$set] = 0; 00937 00938 if ($text && $text!='none') { 00939 $numSets++; 00940 $sumTextHeight += $size['height']; 00941 } 00942 00943 if ($size['width'] > $this->calculated['legend']['boundary_box_max']['width']) 00944 $this->calculated['legend']['boundary_box_max'] = $size; 00945 } 00946 00947 $offset = $this->parameter['legend_offset']; // offset in pixels of legend box from graph border. 00948 $padding = $this->parameter['legend_padding']; // padding in pixels around legend text. 00949 $textWidth = $this->calculated['legend']['boundary_box_max']['width']; // width of largest legend item. 00950 $textHeight = $this->calculated['legend']['boundary_box_max']['height']; // use height as size to use for colour square in legend. 00951 $width = $padding * 2 + $textWidth + $textHeight * 2; // left and right padding + maximum text width + space for square 00952 $height = ($padding + $textHeight) * $numSets + $padding; // top and bottom padding + padding between text + text. 00953 00954 $this->calculated['legend']['boundary_box_all'] = array('width' => $width, 00955 'height' => $height, 00956 'offset' => $offset, 00957 'reference' => $position); 00958 00959 switch ($position) { // move in right or bottom if legend is outside data plotting area. 00960 case 'outside-top' : 00961 $this->calculated['boundary_box']['right'] -= $offset + $width; // move in right hand side 00962 break; 00963 00964 case 'outside-bottom' : 00965 $this->calculated['boundary_box']['right'] -= $offset + $width; // move in right hand side 00966 break; 00967 00968 case 'outside-left' : 00969 $this->calculated['boundary_box']['bottom'] -= $offset + $height; // move in right hand side 00970 break; 00971 00972 case 'outside-right' : 00973 $this->calculated['boundary_box']['bottom'] -= $offset + $height; // move in right hand side 00974 break; 00975 } 00976 } 00977 00978 function init_y_axis() { 00979 $this->calculated['y_axis_left'] = array(); // array to hold calculated values for y_axis on left. 00980 $this->calculated['y_axis_left']['boundary_box_max'] = $this->get_null_size(); 00981 $this->calculated['y_axis_right'] = array(); // array to hold calculated values for y_axis on right. 00982 $this->calculated['y_axis_right']['boundary_box_max'] = $this->get_null_size(); 00983 00984 $axis_font = $this->parameter['axis_font']; 00985 $axis_size = $this->parameter['axis_size']; 00986 $axis_colour = $this->parameter['axis_colour']; 00987 $axis_angle = $this->parameter['y_axis_angle']; 00988 $y_tick_labels = $this->y_tick_labels; 00989 00990 $this->calculated['y_axis_left']['has_data'] = FALSE; 00991 $this->calculated['y_axis_right']['has_data'] = FALSE; 00992 00993 // find min and max y values. 00994 $minLeft = $this->parameter['y_min_left']; 00995 $maxLeft = $this->parameter['y_max_left']; 00996 $minRight = $this->parameter['y_min_right']; 00997 $maxRight = $this->parameter['y_max_right']; 00998 $dataLeft = array(); 00999 $dataRight = array(); 01000 foreach ($this->y_order as $order => $set) { 01001 if (isset($this->y_format[$set]['y_axis']) && $this->y_format[$set]['y_axis'] == 'right') { 01002 $this->calculated['y_axis_right']['has_data'] = TRUE; 01003 $dataRight = array_merge($dataRight, $this->y_data[$set]); 01004 } else { 01005 $this->calculated['y_axis_left']['has_data'] = TRUE; 01006 $dataLeft = array_merge($dataLeft, $this->y_data[$set]); 01007 } 01008 } 01009 $dataLeftRange = $this->find_range($dataLeft, $minLeft, $maxLeft, $this->parameter['y_resolution_left']); 01010 $dataRightRange = $this->find_range($dataRight, $minRight, $maxRight, $this->parameter['y_resolution_right']); 01011 $minLeft = $dataLeftRange['min']; 01012 $maxLeft = $dataLeftRange['max']; 01013 $minRight = $dataRightRange['min']; 01014 $maxRight = $dataRightRange['max']; 01015 01016 $this->calculated['y_axis_left']['min'] = $minLeft; 01017 $this->calculated['y_axis_left']['max'] = $maxLeft; 01018 $this->calculated['y_axis_right']['min'] = $minRight; 01019 $this->calculated['y_axis_right']['max'] = $maxRight; 01020 01021 $stepLeft = ($maxLeft - $minLeft) / ($this->parameter['y_axis_gridlines'] - 1); 01022 $startLeft = $minLeft; 01023 $step_right = ($maxRight - $minRight) / ($this->parameter['y_axis_gridlines'] - 1); 01024 $start_right = $minRight; 01025 01026 if ($this->parameter['y_axis_text_left']) { 01027 for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { // calculate y axis text sizes 01028 // left y axis 01029 if ($y_tick_labels) { 01030 $value = $y_tick_labels[$i]; 01031 } else { 01032 $value = number_format($startLeft, $this->parameter['y_decimal_left'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); 01033 } 01034 $this->calculated['y_axis_left']['data'][$i] = $startLeft; 01035 $this->calculated['y_axis_left']['text'][$i] = $value; // text is formatted raw data 01036 01037 $size = $this->get_boundaryBox( 01038 array('points' => $axis_size, 01039 'font' => $axis_font, 01040 'angle' => $axis_angle, 01041 'colour' => $axis_colour, 01042 'text' => $value)); 01043 $this->calculated['y_axis_left']['boundary_box'][$i] = $size; 01044 01045 if ($size['height'] > $this->calculated['y_axis_left']['boundary_box_max']['height']) 01046 $this->calculated['y_axis_left']['boundary_box_max']['height'] = $size['height']; 01047 if ($size['width'] > $this->calculated['y_axis_left']['boundary_box_max']['width']) 01048 $this->calculated['y_axis_left']['boundary_box_max']['width'] = $size['width']; 01049 01050 $startLeft += $stepLeft; 01051 } 01052 $this->calculated['boundary_box']['left'] += $this->calculated['y_axis_left']['boundary_box_max']['width'] 01053 + $this->parameter['y_inner_padding']; 01054 } 01055 01056 if ($this->parameter['y_axis_text_right']) { 01057 for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { // calculate y axis text sizes 01058 // right y axis 01059 $value = number_format($start_right, $this->parameter['y_decimal_right'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); 01060 $this->calculated['y_axis_right']['data'][$i] = $start_right; 01061 $this->calculated['y_axis_right']['text'][$i] = $value; // text is formatted raw data 01062 $size = $this->get_boundaryBox( 01063 array('points' => $axis_size, 01064 'font' => $axis_font, 01065 'angle' => $axis_angle, 01066 'colour' => $axis_colour, 01067 'text' => $value)); 01068 $this->calculated['y_axis_right']['boundary_box'][$i] = $size; 01069 01070 if ($size['height'] > $this->calculated['y_axis_right']['boundary_box_max']['height']) 01071 $this->calculated['y_axis_right']['boundary_box_max'] = $size; 01072 if ($size['width'] > $this->calculated['y_axis_right']['boundary_box_max']['width']) 01073 $this->calculated['y_axis_right']['boundary_box_max']['width'] = $size['width']; 01074 01075 $start_right += $step_right; 01076 } 01077 $this->calculated['boundary_box']['right'] -= $this->calculated['y_axis_right']['boundary_box_max']['width'] 01078 + $this->parameter['y_inner_padding']; 01079 } 01080 } 01081 01082 function init_x_axis() { 01083 $this->calculated['x_axis'] = array(); // array to hold calculated values for x_axis. 01084 $this->calculated['x_axis']['boundary_box_max'] = array('height' => 0, 'width' => 0); 01085 01086 $axis_font = $this->parameter['axis_font']; 01087 $axis_size = $this->parameter['axis_size']; 01088 $axis_colour = $this->parameter['axis_colour']; 01089 $axis_angle = $this->parameter['x_axis_angle']; 01090 01091 // check whether to treat x axis as numeric 01092 if ($this->parameter['x_axis_gridlines'] == 'auto') { // auto means text based x_axis, not numeric... 01093 $this->calculated['x_axis']['num_ticks'] = sizeof($this->x_data); 01094 $data = $this->x_data; 01095 for ($i=0; $i < $this->calculated['x_axis']['num_ticks']; $i++) { 01096 $value = array_shift($data); // grab value from begin of array 01097 $this->calculated['x_axis']['data'][$i] = $value; 01098 $this->calculated['x_axis']['text'][$i] = $value; // raw data and text are both the same in this case 01099 $size = $this->get_boundaryBox( 01100 array('points' => $axis_size, 01101 'font' => $axis_font, 01102 'angle' => $axis_angle, 01103 'colour' => $axis_colour, 01104 'text' => $value)); 01105 $this->calculated['x_axis']['boundary_box'][$i] = $size; 01106 if ($size['height'] > $this->calculated['x_axis']['boundary_box_max']['height']) 01107 $this->calculated['x_axis']['boundary_box_max'] = $size; 01108 } 01109 01110 } else { // x axis is numeric so find max min values... 01111 $this->calculated['x_axis']['num_ticks'] = $this->parameter['x_axis_gridlines']; 01112 01113 $min = $this->parameter['x_min']; 01114 $max = $this->parameter['x_max']; 01115 $data = array(); 01116 $data = $this->find_range($this->x_data, $min, $max, $this->parameter['x_resolution']); 01117 $min = $data['min']; 01118 $max = $data['max']; 01119 $this->calculated['x_axis']['min'] = $min; 01120 $this->calculated['x_axis']['max'] = $max; 01121 01122 $step = ($max - $min) / ($this->calculated['x_axis']['num_ticks'] - 1); 01123 $start = $min; 01124 01125 for ($i = 0; $i < $this->calculated['x_axis']['num_ticks']; $i++) { // calculate x axis text sizes 01126 $value = number_format($start, $this->parameter['xDecimal'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); 01127 $this->calculated['x_axis']['data'][$i] = $start; 01128 $this->calculated['x_axis']['text'][$i] = $value; // text is formatted raw data 01129 01130 $size = $this->get_boundaryBox( 01131 array('points' => $axis_size, 01132 'font' => $axis_font, 01133 'angle' => $axis_angle, 01134 'colour' => $axis_colour, 01135 'text' => $value)); 01136 $this->calculated['x_axis']['boundary_box'][$i] = $size; 01137 01138 if ($size['height'] > $this->calculated['x_axis']['boundary_box_max']['height']) 01139 $this->calculated['x_axis']['boundary_box_max'] = $size; 01140 01141 $start += $step; 01142 } 01143 } 01144 if ($this->parameter['x_axis_text']) 01145 $this->calculated['boundary_box']['bottom'] -= $this->calculated['x_axis']['boundary_box_max']['height'] 01146 + $this->parameter['x_inner_padding']; 01147 } 01148 01149 // find max and min values for a data array given the resolution. 01150 function find_range($data, $min, $max, $resolution) { 01151 if (sizeof($data) == 0 ) return array('min' => 0, 'max' => 0); 01152 foreach ($data as $key => $value) { 01153 if ($value=='none') continue; 01154 if ($value > $max) $max = $value; 01155 if ($value < $min) $min = $value; 01156 } 01157 01158 if ($max == 0) { 01159 $factor = 1; 01160 } else { 01161 if ($max < 0) $factor = - pow(10, (floor(log10(abs($max))) + $resolution) ); 01162 else $factor = pow(10, (floor(log10(abs($max))) - $resolution) ); 01163 } 01164 if ($factor > 0.1) { // To avoid some wierd rounding errors (Moodle) 01165 $factor = round($factor * 1000.0) / 1000.0; // To avoid some wierd rounding errors (Moodle) 01166 } // To avoid some wierd rounding errors (Moodle) 01167 01168 $max = $factor * @ceil($max / $factor); 01169 $min = $factor * @floor($min / $factor); 01170 01171 //print "max=$max, min=$min<br />"; 01172 01173 return array('min' => $min, 'max' => $max); 01174 } 01175 01176 function graph() { 01177 if (func_num_args() == 2) { 01178 $this->parameter['width'] = func_get_arg(0); 01179 $this->parameter['height'] = func_get_arg(1); 01180 } 01181 //$this->boundaryBox = array( 01182 $this->calculated['boundary_box'] = array( 01183 'left' => 0, 01184 'top' => 0, 01185 'right' => $this->parameter['width'] - 1, 01186 'bottom' => $this->parameter['height'] - 1); 01187 01188 $this->init_colours(); 01189 01190 //ImageColorTransparent($this->image, $this->colour['white']); // colour for transparency 01191 } 01192 01193 function print_TTF($message) { 01194 $points = $message['points']; 01195 $angle = $message['angle']; 01196 $text = $message['text']; 01197 $colour = $this->colour[$message['colour']]; 01198 $font = $this->parameter['path_to_fonts'].$message['font']; 01199 01200 $x = $message['boundary_box']['x']; 01201 $y = $message['boundary_box']['y']; 01202 $offsetX = $message['boundary_box']['offsetX']; 01203 $offsetY = $message['boundary_box']['offsetY']; 01204 $height = $message['boundary_box']['height']; 01205 $width = $message['boundary_box']['width']; 01206 $reference = $message['boundary_box']['reference']; 01207 01208 switch ($reference) { 01209 case 'top-left': 01210 case 'left-top': 01211 $y += $height - $offsetY; 01212 //$y += $offsetY; 01213 $x += $offsetX; 01214 break; 01215 case 'left-center': 01216 $y += ($height / 2) - $offsetY; 01217 $x += $offsetX; 01218 break; 01219 case 'left-bottom': 01220 $y -= $offsetY; 01221 $x += $offsetX; 01222 break; 01223 case 'top-center': 01224 $y += $height - $offsetY; 01225 $x -= ($width / 2) - $offsetX; 01226 break; 01227 case 'top-right': 01228 case 'right-top': 01229 $y += $height - $offsetY; 01230 $x -= $width - $offsetX; 01231 break; 01232 case 'right-center': 01233 $y += ($height / 2) - $offsetY; 01234 $x -= $width - $offsetX; 01235 break; 01236 case 'right-bottom': 01237 $y -= $offsetY; 01238 $x -= $width - $offsetX; 01239 break; 01240 case 'bottom-center': 01241 $y -= $offsetY; 01242 $x -= ($width / 2) - $offsetX; 01243 break; 01244 default: 01245 $y = 0; 01246 $x = 0; 01247 break; 01248 } 01249 // start of Moodle addition 01250 $textlib = textlib_get_instance(); 01251 $text = $textlib->utf8_to_entities($text, true, true); //does not work with hex entities! 01252 // end of Moodle addition 01253 ImageTTFText($this->image, $points, $angle, $x, $y, $colour, $font, $text); 01254 } 01255 01256 // move boundaryBox to coordinates specified 01257 function update_boundaryBox(&$boundaryBox, $coords) { 01258 $width = $boundaryBox['width']; 01259 $height = $boundaryBox['height']; 01260 $x = $coords['x']; 01261 $y = $coords['y']; 01262 $reference = $coords['reference']; 01263 switch ($reference) { 01264 case 'top-left': 01265 case 'left-top': 01266 $top = $y; 01267 $bottom = $y + $height; 01268 $left = $x; 01269 $right = $x + $width; 01270 break; 01271 case 'left-center': 01272 $top = $y - ($height / 2); 01273 $bottom = $y + ($height / 2); 01274 $left = $x; 01275 $right = $x + $width; 01276 break; 01277 case 'left-bottom': 01278 $top = $y - $height; 01279 $bottom = $y; 01280 $left = $x; 01281 $right = $x + $width; 01282 break; 01283 case 'top-center': 01284 $top = $y; 01285 $bottom = $y + $height; 01286 $left = $x - ($width / 2); 01287 $right = $x + ($width / 2); 01288 break; 01289 case 'right-top': 01290 case 'top-right': 01291 $top = $y; 01292 $bottom = $y + $height; 01293 $left = $x - $width; 01294 $right = $x; 01295 break; 01296 case 'right-center': 01297 $top = $y - ($height / 2); 01298 $bottom = $y + ($height / 2); 01299 $left = $x - $width; 01300 $right = $x; 01301 break; 01302 case 'bottom=right': 01303 case 'right-bottom': 01304 $top = $y - $height; 01305 $bottom = $y; 01306 $left = $x - $width; 01307 $right = $x; 01308 break; 01309 default: 01310 $top = 0; 01311 $bottom = $height; 01312 $left = 0; 01313 $right = $width; 01314 break; 01315 } 01316 01317 $boundaryBox = array_merge($boundaryBox, array('top' => $top, 01318 'bottom' => $bottom, 01319 'left' => $left, 01320 'right' => $right, 01321 'x' => $x, 01322 'y' => $y, 01323 'reference' => $reference)); 01324 } 01325 01326 function get_null_size() { 01327 return array('width' => 0, 01328 'height' => 0, 01329 'offsetX' => 0, 01330 'offsetY' => 0, 01331 //'fontHeight' => 0 01332 ); 01333 } 01334 01335 function get_boundaryBox($message) { 01336 $points = $message['points']; 01337 $angle = $message['angle']; 01338 $font = $this->parameter['path_to_fonts'].$message['font']; 01339 $text = $message['text']; 01340 01341 //print ('get_boundaryBox'); 01342 //expandPre($message); 01343 01344 // get font size 01345 $bounds = ImageTTFBBox($points, $angle, $font, "W"); 01346 if ($angle < 0) { 01347 $fontHeight = abs($bounds[7]-$bounds[1]); 01348 } else if ($angle > 0) { 01349 $fontHeight = abs($bounds[1]-$bounds[7]); 01350 } else { 01351 $fontHeight = abs($bounds[7]-$bounds[1]); 01352 } 01353 01354 // get boundary box and offsets for printing at an angle 01355 // start of Moodle addition 01356 $textlib = textlib_get_instance(); 01357 $text = $textlib->utf8_to_entities($text, true, true); //gd does not work with hex entities! 01358 // end of Moodle addition 01359 $bounds = ImageTTFBBox($points, $angle, $font, $text); 01360 01361 if ($angle < 0) { 01362 $width = abs($bounds[4]-$bounds[0]); 01363 $height = abs($bounds[3]-$bounds[7]); 01364 $offsetY = abs($bounds[3]-$bounds[1]); 01365 $offsetX = 0; 01366 01367 } else if ($angle > 0) { 01368 $width = abs($bounds[2]-$bounds[6]); 01369 $height = abs($bounds[1]-$bounds[5]); 01370 $offsetY = 0; 01371 $offsetX = abs($bounds[0]-$bounds[6]); 01372 01373 } else { 01374 $width = abs($bounds[4]-$bounds[6]); 01375 $height = abs($bounds[7]-$bounds[1]); 01376 $offsetY = $bounds[1]; 01377 $offsetX = 0; 01378 } 01379 01380 //return values 01381 return array('width' => $width, 01382 'height' => $height, 01383 'offsetX' => $offsetX, 01384 'offsetY' => $offsetY, 01385 //'fontHeight' => $fontHeight 01386 ); 01387 } 01388 01389 function draw_rectangle($border, $colour, $type) { 01390 $colour = $this->colour[$colour]; 01391 switch ($type) { 01392 case 'fill': // fill the rectangle 01393 ImageFilledRectangle($this->image, $border['left'], $border['top'], $border['right'], $border['bottom'], $colour); 01394 break; 01395 case 'box': // all sides 01396 ImageRectangle($this->image, $border['left'], $border['top'], $border['right'], $border['bottom'], $colour); 01397 break; 01398 case 'axis': // bottom x axis and left y axis 01399 ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); 01400 ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); 01401 break; 01402 case 'y': // left y axis only 01403 case 'y-left': 01404 ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); 01405 break; 01406 case 'y-right': // right y axis only 01407 ImageLine($this->image, $border['right'], $border['top'], $border['right'], $border['bottom'], $colour); 01408 break; 01409 case 'x': // bottom x axis only 01410 ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); 01411 break; 01412 case 'u': // u shaped. bottom x axis and both left and right y axis. 01413 ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); 01414 ImageLine($this->image, $border['right'], $border['top'], $border['right'], $border['bottom'], $colour); 01415 ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); 01416 break; 01417 01418 } 01419 } 01420 01421 function init_colours() { 01422 $this->image = ImageCreate($this->parameter['width'], $this->parameter['height']); 01423 // standard colours 01424 $this->colour['white'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0xFF); // first colour is background colour. 01425 $this->colour['black'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0x00); 01426 $this->colour['maroon'] = ImageColorAllocate ($this->image, 0x80, 0x00, 0x00); 01427 $this->colour['green'] = ImageColorAllocate ($this->image, 0x00, 0x80, 0x00); 01428 $this->colour['ltgreen'] = ImageColorAllocate ($this->image, 0x52, 0xF1, 0x7F); 01429 $this->colour['ltltgreen']= ImageColorAllocate ($this->image, 0x99, 0xFF, 0x99); 01430 $this->colour['olive'] = ImageColorAllocate ($this->image, 0x80, 0x80, 0x00); 01431 $this->colour['navy'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0x80); 01432 $this->colour['purple'] = ImageColorAllocate ($this->image, 0x80, 0x00, 0x80); 01433 $this->colour['gray'] = ImageColorAllocate ($this->image, 0x80, 0x80, 0x80); 01434 $this->colour['red'] = ImageColorAllocate ($this->image, 0xFF, 0x00, 0x00); 01435 $this->colour['ltred'] = ImageColorAllocate ($this->image, 0xFF, 0x99, 0x99); 01436 $this->colour['ltltred'] = ImageColorAllocate ($this->image, 0xFF, 0xCC, 0xCC); 01437 $this->colour['orange'] = ImageColorAllocate ($this->image, 0xFF, 0x66, 0x00); 01438 $this->colour['ltorange'] = ImageColorAllocate ($this->image, 0xFF, 0x99, 0x66); 01439 $this->colour['ltltorange'] = ImageColorAllocate ($this->image, 0xFF, 0xcc, 0x99); 01440 $this->colour['lime'] = ImageColorAllocate ($this->image, 0x00, 0xFF, 0x00); 01441 $this->colour['yellow'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0x00); 01442 $this->colour['blue'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0xFF); 01443 $this->colour['ltblue'] = ImageColorAllocate ($this->image, 0x00, 0xCC, 0xFF); 01444 $this->colour['ltltblue'] = ImageColorAllocate ($this->image, 0x99, 0xFF, 0xFF); 01445 $this->colour['fuchsia'] = ImageColorAllocate ($this->image, 0xFF, 0x00, 0xFF); 01446 $this->colour['aqua'] = ImageColorAllocate ($this->image, 0x00, 0xFF, 0xFF); 01447 //$this->colour['white'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0xFF); 01448 // shades of gray 01449 $this->colour['grayF0'] = ImageColorAllocate ($this->image, 0xF0, 0xF0, 0xF0); 01450 $this->colour['grayEE'] = ImageColorAllocate ($this->image, 0xEE, 0xEE, 0xEE); 01451 $this->colour['grayDD'] = ImageColorAllocate ($this->image, 0xDD, 0xDD, 0xDD); 01452 $this->colour['grayCC'] = ImageColorAllocate ($this->image, 0xCC, 0xCC, 0xCC); 01453 $this->colour['gray33'] = ImageColorAllocate ($this->image, 0x33, 0x33, 0x33); 01454 $this->colour['gray66'] = ImageColorAllocate ($this->image, 0x66, 0x66, 0x66); 01455 $this->colour['gray99'] = ImageColorAllocate ($this->image, 0x99, 0x99, 0x99); 01456 01457 $this->colour['none'] = 'none'; 01458 return true; 01459 } 01460 01461 function output() { 01462 if ($this->debug) { // for debugging purposes. 01463 //expandPre($this->graph); 01464 //expandPre($this->y_data); 01465 //expandPre($this->x_data); 01466 //expandPre($this->parameter); 01467 } else { 01468 01469 $expiresSeconds = $this->parameter['seconds_to_live']; 01470 $expiresHours = $this->parameter['hours_to_live']; 01471 01472 if ($expiresHours || $expiresSeconds) { 01473 $now = mktime (date("H"),date("i"),date("s"),date("m"),date("d"),date("Y")); 01474 $expires = mktime (date("H")+$expiresHours,date("i"),date("s")+$expiresSeconds,date("m"),date("d"),date("Y")); 01475 $expiresGMT = gmdate('D, d M Y H:i:s', $expires).' GMT'; 01476 $lastModifiedGMT = gmdate('D, d M Y H:i:s', $now).' GMT'; 01477 01478 Header('Last-modified: '.$lastModifiedGMT); 01479 Header('Expires: '.$expiresGMT); 01480 } 01481 01482 if ($this->parameter['file_name'] == 'none') { 01483 switch ($this->parameter['output_format']) { 01484 case 'GIF': 01485 Header("Content-type: image/gif"); // GIF??. switch to PNG guys!! 01486 ImageGIF($this->image); 01487 break; 01488 case 'JPEG': 01489 Header("Content-type: image/jpeg"); // JPEG for line art??. included for completeness. 01490 ImageJPEG($this->image); 01491 break; 01492 default: 01493 Header("Content-type: image/png"); // preferred output format 01494 ImagePNG($this->image); 01495 break; 01496 } 01497 } else { 01498 switch ($this->parameter['output_format']) { 01499 case 'GIF': 01500 ImageGIF($this->image, $this->parameter['file_name'].'.gif'); 01501 break; 01502 case 'JPEG': 01503 ImageJPEG($this->image, $this->parameter['file_name'].'.jpg'); 01504 break; 01505 default: 01506 ImagePNG($this->image, $this->parameter['file_name'].'.png'); 01507 break; 01508 } 01509 } 01510 01511 ImageDestroy($this->image); 01512 } 01513 } // function output 01514 01515 function init_variable(&$variable, $value, $default) { 01516 if (!empty($value)) $variable = $value; 01517 else if (isset($default)) $variable = $default; 01518 else unset($variable); 01519 } 01520 01521 // plot a point. options include square, circle, diamond, triangle, and dot. offset is used for drawing shadows. 01522 // for diamonds and triangles the size should be an even number to get nice look. if odd the points are crooked. 01523 function plot($x, $y, $type, $size, $colour, $offset) { 01524 //print("drawing point of type: $type, at offset: $offset"); 01525 $u = $x + $offset; 01526 $v = $this->calculated['inner_border']['bottom'] - $y + $offset; 01527 $half = $size / 2; 01528 01529 switch ($type) { 01530 case 'square': 01531 ImageFilledRectangle($this->image, $u-$half, $v-$half, $u+$half, $v+$half, $this->colour[$colour]); 01532 break; 01533 case 'square-open': 01534 ImageRectangle($this->image, $u-$half, $v-$half, $u+$half, $v+$half, $this->colour[$colour]); 01535 break; 01536 case 'circle': 01537 ImageArc($this->image, $u, $v, $size, $size, 0, 360, $this->colour[$colour]); 01538 ImageFillToBorder($this->image, $u, $v, $this->colour[$colour], $this->colour[$colour]); 01539 break; 01540 case 'circle-open': 01541 ImageArc($this->image, $u, $v, $size, $size, 0, 360, $this->colour[$colour]); 01542 break; 01543 case 'diamond': 01544 ImageFilledPolygon($this->image, array($u, $v-$half, $u+$half, $v, $u, $v+$half, $u-$half, $v), 4, $this->colour[$colour]); 01545 break; 01546 case 'diamond-open': 01547 ImagePolygon($this->image, array($u, $v-$half, $u+$half, $v, $u, $v+$half, $u-$half, $v), 4, $this->colour[$colour]); 01548 break; 01549 case 'triangle': 01550 ImageFilledPolygon($this->image, array($u, $v-$half, $u+$half, $v+$half, $u-$half, $v+$half), 3, $this->colour[$colour]); 01551 break; 01552 case 'triangle-open': 01553 ImagePolygon($this->image, array($u, $v-$half, $u+$half, $v+$half, $u-$half, $v+$half), 3, $this->colour[$colour]); 01554 break; 01555 case 'dot': 01556 ImageSetPixel($this->image, $u, $v, $this->colour[$colour]); 01557 break; 01558 } 01559 } 01560 01561 function bar($x, $y, $type, $size, $colour, $offset, $index, $yoffset) { 01562 $index_offset = $this->calculated['bar_offset_index'][$index]; 01563 if ( $yoffset ) { 01564 $bar_offsetx = 0; 01565 } else { 01566 $bar_offsetx = $this->calculated['bar_offset_x'][$index_offset]; 01567 } 01568 //$this->dbug("drawing bar at offset = $offset : index = $index: bar_offsetx = $bar_offsetx"); 01569 01570 $span = ($this->calculated['bar_width'] * $size) / 2; 01571 $x_left = $x + $bar_offsetx - $span; 01572 $x_right = $x + $bar_offsetx + $span; 01573 01574 if ($this->parameter['zero_axis'] != 'none') { 01575 $zero = $this->calculated['zero_axis']; 01576 if ($this->parameter['shadow_below_axis'] ) $zero += $offset; 01577 $u_left = $x_left + $offset; 01578 $u_right = $x_right + $offset - 1; 01579 $v = $this->calculated['boundary_box']['bottom'] - $y + $offset; 01580 01581 if ($v > $zero) { 01582 $top = $zero +1; 01583 $bottom = $v; 01584 } else { 01585 $top = $v; 01586 $bottom = $zero - 1; 01587 } 01588 01589 switch ($type) { 01590 case 'open': 01591 //ImageRectangle($this->image, round($u_left), $top, round($u_right), $bottom, $this->colour[$colour]); 01592 if ($v > $zero) 01593 ImageRectangle($this->image, round($u_left), $bottom, round($u_right), $bottom, $this->colour[$colour]); 01594 else 01595 ImageRectangle($this->image, round($u_left), $top, round($u_right), $top, $this->colour[$colour]); 01596 ImageRectangle($this->image, round($u_left), $top, round($u_left), $bottom, $this->colour[$colour]); 01597 ImageRectangle($this->image, round($u_right), $top, round($u_right), $bottom, $this->colour[$colour]); 01598 break; 01599 case 'fill': 01600 ImageFilledRectangle($this->image, round($u_left), $top, round($u_right), $bottom, $this->colour[$colour]); 01601 break; 01602 } 01603 01604 } else { 01605 01606 $bottom = $this->calculated['boundary_box']['bottom']; 01607 if ($this->parameter['shadow_below_axis'] ) $bottom += $offset; 01608 if ($this->parameter['inner_border'] != 'none') $bottom -= 1; // 1 pixel above bottom if border is to be drawn. 01609 $u_left = $x_left + $offset; 01610 $u_right = $x_right + $offset - 1; 01611 $v = $this->calculated['boundary_box']['bottom'] - $y + $offset; 01612 01613 // Moodle addition, plus the function parameter yoffset 01614 if ($yoffset) { // Moodle 01615 $yoffset = $yoffset - round(($bottom - $v) / 2.0); // Moodle 01616 $bottom -= $yoffset; // Moodle 01617 $v -= $yoffset; // Moodle 01618 } // Moodle 01619 01620 switch ($type) { 01621 case 'open': 01622 ImageRectangle($this->image, round($u_left), $v, round($u_right), $bottom, $this->colour[$colour]); 01623 break; 01624 case 'fill': 01625 ImageFilledRectangle($this->image, round($u_left), $v, round($u_right), $bottom, $this->colour[$colour]); 01626 break; 01627 } 01628 } 01629 } 01630 01631 function area($x_start, $y_start, $x_end, $y_end, $type, $colour, $offset) { 01632 //dbug("drawing area type: $type, at offset: $offset"); 01633 if ($this->parameter['zero_axis'] != 'none') { 01634 $bottom = $this->calculated['boundary_box']['bottom']; 01635 $zero = $this->calculated['zero_axis']; 01636 if ($this->parameter['shadow_below_axis'] ) $zero += $offset; 01637 $u_start = $x_start + $offset; 01638 $u_end = $x_end + $offset; 01639 $v_start = $bottom - $y_start + $offset; 01640 $v_end = $bottom - $y_end + $offset; 01641 switch ($type) { 01642 case 'fill': 01643 // draw it this way 'cos the FilledPolygon routine seems a bit buggy. 01644 ImageFilledPolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $zero, $u_start, $zero), 4, $this->colour[$colour]); 01645 ImagePolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $zero, $u_start, $zero), 4, $this->colour[$colour]); 01646 break; 01647 case 'open': 01648 //ImagePolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $zero, $u_start, $zero), 4, $this->colour[$colour]); 01649 ImageLine($this->image, $u_start, $v_start, $u_end, $v_end, $this->colour[$colour]); 01650 ImageLine($this->image, $u_start, $v_start, $u_start, $zero, $this->colour[$colour]); 01651 ImageLine($this->image, $u_end, $v_end, $u_end, $zero, $this->colour[$colour]); 01652 break; 01653 } 01654 } else { 01655 $bottom = $this->calculated['boundary_box']['bottom']; 01656 $u_start = $x_start + $offset; 01657 $u_end = $x_end + $offset; 01658 $v_start = $bottom - $y_start + $offset; 01659 $v_end = $bottom - $y_end + $offset; 01660 01661 if ($this->parameter['shadow_below_axis'] ) $bottom += $offset; 01662 if ($this->parameter['inner_border'] != 'none') $bottom -= 1; // 1 pixel above bottom if border is to be drawn. 01663 switch ($type) { 01664 case 'fill': 01665 ImageFilledPolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $bottom, $u_start, $bottom), 4, $this->colour[$colour]); 01666 break; 01667 case 'open': 01668 ImagePolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $bottom, $u_start, $bottom), 4, $this->colour[$colour]); 01669 break; 01670 } 01671 } 01672 } 01673 01674 function line($x_start, $y_start, $x_end, $y_end, $type, $brush_type, $brush_size, $colour, $offset) { 01675 //dbug("drawing line of type: $type, at offset: $offset"); 01676 $u_start = $x_start + $offset; 01677 $v_start = $this->calculated['boundary_box']['bottom'] - $y_start + $offset; 01678 $u_end = $x_end + $offset; 01679 $v_end = $this->calculated['boundary_box']['bottom'] - $y_end + $offset; 01680 01681 switch ($type) { 01682 case 'brush': 01683 $this->draw_brush_line($u_start, $v_start, $u_end, $v_end, $brush_size, $brush_type, $colour); 01684 break; 01685 case 'line' : 01686 ImageLine($this->image, $u_start, $v_start, $u_end, $v_end, $this->colour[$colour]); 01687 break; 01688 case 'dash': 01689 ImageDashedLine($this->image, $u_start, $v_start, $u_end, $v_end, $this->colour[$colour]); 01690 break; 01691 } 01692 } 01693 01694 // function to draw line. would prefer to use gdBrush but this is not supported yet. 01695 function draw_brush_line($x0, $y0, $x1, $y1, $size, $type, $colour) { 01696 //$this->dbug("line: $x0, $y0, $x1, $y1"); 01697 $dy = $y1 - $y0; 01698 $dx = $x1 - $x0; 01699 $t = 0; 01700 $watchdog = 1024; // precaution to prevent infinite loops. 01701 01702 $this->draw_brush($x0, $y0, $size, $type, $colour); 01703 if (abs($dx) > abs($dy)) { // slope < 1 01704 //$this->dbug("slope < 1"); 01705 $m = $dy / $dx; // compute slope 01706 $t += $y0; 01707 $dx = ($dx < 0) ? -1 : 1; 01708 $m *= $dx; 01709 while (round($x0) != round($x1)) { 01710 if (!$watchdog--) break; 01711 $x0 += $dx; // step to next x value 01712 $t += $m; // add slope to y value 01713 $y = round($t); 01714 //$this->dbug("x0=$x0, x1=$x1, y=$y watchdog=$watchdog"); 01715 $this->draw_brush($x0, $y, $size, $type, $colour); 01716 01717 } 01718 } else { // slope >= 1 01719 //$this->dbug("slope >= 1"); 01720 $m = $dx / $dy; // compute slope 01721 $t += $x0; 01722 $dy = ($dy < 0) ? -1 : 1; 01723 $m *= $dy; 01724 while (round($y0) != round($y1)) { 01725 if (!$watchdog--) break; 01726 $y0 += $dy; // step to next y value 01727 $t += $m; // add slope to x value 01728 $x = round($t); 01729 //$this->dbug("x=$x, y0=$y0, y1=$y1 watchdog=$watchdog"); 01730 $this->draw_brush($x, $y0, $size, $type, $colour); 01731 01732 } 01733 } 01734 } 01735 01736 function draw_brush($x, $y, $size, $type, $colour) { 01737 $x = round($x); 01738 $y = round($y); 01739 $half = round($size / 2); 01740 switch ($type) { 01741 case 'circle': 01742 ImageArc($this->image, $x, $y, $size, $size, 0, 360, $this->colour[$colour]); 01743 ImageFillToBorder($this->image, $x, $y, $this->colour[$colour], $this->colour[$colour]); 01744 break; 01745 case 'square': 01746 ImageFilledRectangle($this->image, $x-$half, $y-$half, $x+$half, $y+$half, $this->colour[$colour]); 01747 break; 01748 case 'vertical': 01749 ImageFilledRectangle($this->image, $x, $y-$half, $x+1, $y+$half, $this->colour[$colour]); 01750 break; 01751 case 'horizontal': 01752 ImageFilledRectangle($this->image, $x-$half, $y, $x+$half, $y+1, $this->colour[$colour]); 01753 break; 01754 case 'slash': 01755 ImageFilledPolygon($this->image, array($x+$half, $y-$half, 01756 $x+$half+1, $y-$half, 01757 $x-$half+1, $y+$half, 01758 $x-$half, $y+$half 01759 ), 4, $this->colour[$colour]); 01760 break; 01761 case 'backslash': 01762 ImageFilledPolygon($this->image, array($x-$half, $y-$half, 01763 $x-$half+1, $y-$half, 01764 $x+$half+1, $y+$half, 01765 $x+$half, $y+$half 01766 ), 4, $this->colour[$colour]); 01767 break; 01768 default: 01769 @eval($type); // user can create own brush script. 01770 } 01771 } 01772 01773 } // class graph