PageRenderTime 89ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/administrator/components/com_sm2emailmarketing/includes/phplot/phplot.php

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