PageRenderTime 31ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/Upload/inc/class_graph.php

https://gitlab.com/Conors99/ppm-1.8
PHP | 337 lines | 158 code | 47 blank | 132 comment | 24 complexity | b35afae1740e15ec21aad1a23b5ab337 MD5 | raw file
  1. <?php
  2. /**
  3. * MyBB 1.8
  4. * Copyright 2014 MyBB Group, All Rights Reserved
  5. *
  6. * Website: http://www.mybb.com
  7. * License: http://www.mybb.com/about/license
  8. *
  9. */
  10. class Graph {
  11. /**
  12. * The width of the image.
  13. *
  14. * @var integer
  15. */
  16. public $img_width = 1000;
  17. /**
  18. * The height of the image.
  19. *
  20. * @var integer
  21. */
  22. public $img_height = 300;
  23. /**
  24. * The image resource handle.
  25. *
  26. * @var resource
  27. */
  28. private $im;
  29. /**
  30. * The amount of x pixels to start inside the image for the graph
  31. *
  32. * @var integer
  33. */
  34. public $inside_x = 65;
  35. /**
  36. * The amount of y pixels to start inside the image for the graph
  37. *
  38. * @var integer
  39. */
  40. public $inside_y = 30;
  41. /**
  42. * The width of the inside graph
  43. *
  44. * @var integer
  45. */
  46. public $inside_width = 930;
  47. /**
  48. * The height of the inside graph
  49. *
  50. * @var integer
  51. */
  52. public $inside_height = 220;
  53. /**
  54. * The x, y points for the graph
  55. *
  56. * @var array
  57. */
  58. public $points = array();
  59. /**
  60. * The corresponding x labels for the graph
  61. *
  62. * @var array
  63. */
  64. public $x_labels = array();
  65. /**
  66. * The bottom label for the graph
  67. *
  68. * @var string
  69. */
  70. public $bottom_label = "";
  71. /**
  72. * Constructor of class. Initializes the barebore graph.
  73. */
  74. public function __construct()
  75. {
  76. // Setup initial graph layout
  77. // Check for GD >= 2, create base image
  78. if(gd_version() >= 2)
  79. {
  80. $this->im = imagecreatetruecolor($this->img_width, $this->img_height);
  81. }
  82. else
  83. {
  84. $this->im = imagecreate($this->img_width, $this->img_height);
  85. }
  86. // No GD support, die.
  87. if(!$this->im)
  88. {
  89. return;
  90. }
  91. if(function_exists("imageantialias"))
  92. {
  93. imageantialias($this->im, true);
  94. }
  95. // Fill the background
  96. imagefill($this->im, 0, 0, $this->color(239, 239, 239));
  97. // Create our internal working graph box
  98. $inside_end_x = $this->inside_x+$this->inside_width;
  99. $inside_end_y = $this->inside_y+$this->inside_height;
  100. $this->image_create_rectangle($this->inside_x, $this->inside_y, $inside_end_x, $inside_end_y, 4, $this->color(254, 254, 254));
  101. // Draw our three lines inside our internal working graph area
  102. for($i = 1; $i < 4; ++$i)
  103. {
  104. $y_value = $this->inside_y+(($this->inside_height/4)*$i);
  105. imageline($this->im, $this->inside_x, $y_value, $inside_end_x, $y_value, $this->color(185, 185, 185));
  106. }
  107. }
  108. /**
  109. * Select and allocate a color to the internal image resource
  110. *
  111. * @param integer $red The red value
  112. * @param integer $green The green value
  113. * @param integer $blue The blue value
  114. * @return integer A color identifier
  115. */
  116. private function color($red, $green, $blue)
  117. {
  118. return imagecolorallocate($this->im, $red, $green, $blue);
  119. }
  120. /**
  121. * Creates a filled rectangle with optional rounded corners
  122. *
  123. * @param integer $x1 The initial x value
  124. * @param integer $y1 The initial y value
  125. * @param integer $x2 The ending x value
  126. * @param integer $y2 The ending y value
  127. * @param integer $radius The optional radius
  128. * @param integer $color The optional rectangle color (defaults to black)
  129. */
  130. private function image_create_rectangle($x1, $y1, $x2, $y2, $radius=1, $color=null)
  131. {
  132. if($color == null)
  133. {
  134. $color = $this->color(0, 0, 0);
  135. }
  136. // Draw our rectangle
  137. imagefilledrectangle($this->im, $x1, $y1+$radius, $x2, $y2-$radius, $color);
  138. imagefilledrectangle($this->im, $x1+$radius, $y1, $x2-$radius, $y2, $color);
  139. if($radius > 0)
  140. {
  141. $diameter = $radius*2;
  142. // Now draw our four corners on the rectangle
  143. imagefilledellipse($this->im, $x1+$radius, $y1+$radius, $diameter, $diameter, $color);
  144. imagefilledellipse($this->im, $x1+$radius, $y2-$radius, $diameter, $diameter, $color);
  145. imagefilledellipse($this->im, $x2-$radius, $y2-$radius, $diameter, $diameter, $color);
  146. imagefilledellipse($this->im, $x2-$radius, $y1+$radius, $diameter, $diameter, $color);
  147. }
  148. }
  149. /**
  150. * Creates a nicer thick line for angled lines
  151. *
  152. * @param integer $x1 The initial x value
  153. * @param integer $y1 The initial y value
  154. * @param integer $x2 The ending x value
  155. * @param integer $y2 The ending y value
  156. * @param integer $color The optional rectangle color (defaults to black)
  157. * @param integer $thick The optional thickness (defaults to 1)
  158. * @return int
  159. */
  160. private function imagelinethick($x1, $y1, $x2, $y2, $color, $thick = 1)
  161. {
  162. if($thick == 1)
  163. {
  164. return imageline($this->im, $x1, $y1, $x2, $y2, $color);
  165. }
  166. $t = $thick / 2 - 0.5;
  167. if($x1 == $x2 || $y1 == $y2)
  168. {
  169. return imagefilledrectangle($this->im, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color);
  170. }
  171. $k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q
  172. $a = $t / sqrt(1 + pow($k, 2));
  173. $points = array(
  174. round($x1 - (1+$k)*$a), round($y1 + (1-$k)*$a),
  175. round($x1 - (1-$k)*$a), round($y1 - (1+$k)*$a),
  176. round($x2 + (1+$k)*$a), round($y2 - (1-$k)*$a),
  177. round($x2 + (1-$k)*$a), round($y2 + (1+$k)*$a),
  178. );
  179. imagefilledpolygon($this->im, $points, 4, $color);
  180. return imagepolygon($this->im, $points, 4, $color);
  181. }
  182. /**
  183. * Adds an array of x, y points to the internal points array
  184. *
  185. * @param array $points The array of x, y points to add
  186. */
  187. public function add_points($points)
  188. {
  189. $this->points = array_merge($this->points, $points);
  190. }
  191. /**
  192. * Adds an array of x labels to the internal labels array
  193. *
  194. * @param array $labels The array of x labels to add
  195. */
  196. public function add_x_labels($labels)
  197. {
  198. $this->x_labels = array_merge($this->x_labels, $labels);
  199. }
  200. /**
  201. * Sets a bottom label
  202. *
  203. * @param string $label The bottom label to set
  204. */
  205. public function set_bottom_label($label)
  206. {
  207. $this->bottom_label = $label;
  208. }
  209. /**
  210. * Renders the graph to memory
  211. *
  212. */
  213. public function render()
  214. {
  215. // Get our max's and min's
  216. $asorted = $this->points;
  217. sort($asorted, SORT_NUMERIC);
  218. $min = $asorted[0];
  219. $max = $asorted[count($asorted)-1];
  220. // Scale based on how many points we need to shove into 930 pixels of width
  221. $x_delta = $this->inside_width/count($this->points);
  222. // Scale our y axis to 220 pixels
  223. $y_scale_factor = ($max-$min)/$this->inside_height;
  224. // Get our Y initial
  225. $y_initial = $this->inside_y+$this->inside_height;
  226. // Get our scale for finding our points of reference to place our x axis labels
  227. $x_label_scale = ceil(count($this->points)/20);
  228. $x_label_points = array();
  229. $next_y_scaled = 0;
  230. foreach($this->points as $x => $y)
  231. {
  232. if(($x_label_scale == 0 || (($x+1) % $x_label_scale) == 0) && $x != 0)
  233. {
  234. $x_label_points[] = $x;
  235. imagedashedline($this->im, $this->inside_x+($x_delta*$x), 30, $this->inside_x+($x_delta*$x), $y_initial, $this->color(185, 185, 185));
  236. imagefilledellipse($this->im, $this->inside_x+($x_delta*$x), $y_initial-$next_y_scaled+0.5, 8, 8, $this->color(84, 92, 209));
  237. }
  238. // Look ahead to find our next point, if there is one
  239. if(!array_key_exists($x+1, $this->points))
  240. {
  241. break;
  242. }
  243. $next_y = $this->points[$x+1];
  244. if($y_scale_factor == 0)
  245. {
  246. $y_scaled = $next_y_scaled = 0;
  247. }
  248. else
  249. {
  250. $y_scaled = ($y-$min)/$y_scale_factor;
  251. $next_y_scaled = ($next_y-$min)/$y_scale_factor;
  252. }
  253. // Draw our line
  254. $this->imagelinethick($this->inside_x+($x_delta*$x), $y_initial-$y_scaled, $this->inside_x+($x_delta*($x+1)), $y_initial-$next_y_scaled, $this->color(84, 92, 209), 3);
  255. }
  256. // Draw our x labels
  257. foreach($x_label_points as $x)
  258. {
  259. $label = $this->x_labels[$x];
  260. $text_width = imagefontwidth(2)*strlen($label);
  261. $x = $this->inside_x+($x_delta*$x)-($text_width/2);
  262. imagestring($this->im, 2, $x, $y_initial+5, $label, $this->color(0, 0, 0));
  263. }
  264. // Draw our bottom label
  265. imagestring($this->im, 2, ($this->img_width / 2), $y_initial+25, $this->bottom_label, $this->color(0, 0, 0));
  266. if($max > 4)
  267. {
  268. // Draw our y labels
  269. for($i = 1; $i < 4; ++$i)
  270. {
  271. $y_value = $this->inside_y+(($this->inside_height/4)*$i);
  272. imagestring($this->im, 2, 5, $y_value-7, my_number_format(round($min+(($max-$min)/4)*(4-$i))), $this->color(0, 0, 0));
  273. }
  274. }
  275. imagestring($this->im, 2, 5, $this->inside_y+$this->inside_height-7, my_number_format($min), $this->color(0, 0, 0));
  276. imagestring($this->im, 2, 5, $this->inside_y-7, my_number_format($max), $this->color(0, 0, 0));
  277. }
  278. /**
  279. * Outputs the graph to the screen in PNG format
  280. *
  281. */
  282. public function output()
  283. {
  284. // Output the image
  285. header("Content-type: image/png");
  286. imagepng($this->im);
  287. imagedestroy($this->im);
  288. exit;
  289. }
  290. }