/lib/ezc/Graph/src/renderer/axis_label_rotated.php

https://github.com/Proudio-Interactive/phpUnderControl · PHP · 433 lines · 301 code · 33 blank · 99 comment · 21 complexity · 8a4f739e151c73e040412617e9c8650f MD5 · raw file

  1. <?php
  2. /**
  3. * File containing the ezcGraphAxisRotatedLabelRenderer class
  4. *
  5. * @package Graph
  6. * @version 1.4.3
  7. * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
  8. * @license http://ez.no/licenses/new_bsd New BSD License
  9. */
  10. /**
  11. * Can render axis labels rotated, so that more axis labels fit on one axis.
  12. * Produces best results if the axis space was increased, so that more spcae is
  13. * available below the axis.
  14. *
  15. * <code>
  16. * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRotatedLabelRenderer();
  17. *
  18. * // Define angle manually in degree
  19. * $chart->xAxis->axisLabelRenderer->angle = 45;
  20. *
  21. * // Increase axis space
  22. * $chart->xAxis->axisSpace = .2;
  23. * </code>
  24. *
  25. * @property float $angle
  26. * Angle of labels on axis in degrees.
  27. *
  28. * @version 1.4.3
  29. * @package Graph
  30. * @mainclass
  31. */
  32. class ezcGraphAxisRotatedLabelRenderer extends ezcGraphAxisLabelRenderer
  33. {
  34. /**
  35. * Store step array for later coordinate modifications
  36. *
  37. * @var array(ezcGraphStep)
  38. */
  39. protected $steps;
  40. /**
  41. * Store direction for later coordinate modifications
  42. *
  43. * @var ezcGraphVector
  44. */
  45. protected $direction;
  46. /**
  47. * Store coordinate width modifier for later coordinate modifications
  48. *
  49. * @var float
  50. */
  51. protected $widthModifier;
  52. /**
  53. * Store coordinate offset for later coordinate modifications
  54. *
  55. * @var float
  56. */
  57. protected $offset;
  58. /**
  59. * Constructor
  60. *
  61. * @param array $options Default option array
  62. * @return void
  63. * @ignore
  64. */
  65. public function __construct( array $options = array() )
  66. {
  67. parent::__construct( $options );
  68. $this->properties['angle'] = null;
  69. $this->properties['labelOffset'] = true;
  70. }
  71. /**
  72. * __set
  73. *
  74. * @param mixed $propertyName
  75. * @param mixed $propertyValue
  76. * @throws ezcBaseValueException
  77. * If a submitted parameter was out of range or type.
  78. * @throws ezcBasePropertyNotFoundException
  79. * If a the value for the property options is not an instance of
  80. * @return void
  81. * @ignore
  82. */
  83. public function __set( $propertyName, $propertyValue )
  84. {
  85. switch ( $propertyName )
  86. {
  87. case 'angle':
  88. if ( !is_numeric( $propertyValue ) )
  89. {
  90. throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 360' );
  91. }
  92. $reducement = (int) ( $propertyValue - $propertyValue % 360 );
  93. $this->properties['angle'] = (float) $propertyValue - $reducement;
  94. break;
  95. case 'labelOffset':
  96. if ( !is_bool( $propertyValue ) )
  97. {
  98. throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
  99. }
  100. $this->properties[$propertyName] = (bool) $propertyValue;
  101. break;
  102. default:
  103. return parent::__set( $propertyName, $propertyValue );
  104. }
  105. }
  106. /**
  107. * Render Axis labels
  108. *
  109. * Render labels for an axis.
  110. *
  111. * @param ezcGraphRenderer $renderer Renderer used to draw the chart
  112. * @param ezcGraphBoundings $boundings Boundings of the axis
  113. * @param ezcGraphCoordinate $start Axis starting point
  114. * @param ezcGraphCoordinate $end Axis ending point
  115. * @param ezcGraphChartElementAxis $axis Axis instance
  116. * @return void
  117. */
  118. public function renderLabels(
  119. ezcGraphRenderer $renderer,
  120. ezcGraphBoundings $boundings,
  121. ezcGraphCoordinate $start,
  122. ezcGraphCoordinate $end,
  123. ezcGraphChartElementAxis $axis,
  124. ezcGraphBoundings $innerBoundings = null )
  125. {
  126. // receive rendering parameters from axis
  127. $steps = $axis->getSteps();
  128. $this->steps = $steps;
  129. $axisBoundings = new ezcGraphBoundings(
  130. $start->x, $start->y,
  131. $end->x, $end->y
  132. );
  133. // Determine normalized axis direction
  134. $this->direction = new ezcGraphVector(
  135. $end->x - $start->x,
  136. $end->y - $start->y
  137. );
  138. $this->direction->unify();
  139. $axisAngle = -$this->direction->angle( new ezcGraphVector( 1, 0 ) );
  140. // Get axis space
  141. $gridBoundings = null;
  142. list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings );
  143. // Determine optimal angle if none specified
  144. if ( $this->angle === null )
  145. {
  146. $minimumStepWidth = null;
  147. foreach ( $steps as $nr => $step )
  148. {
  149. if ( ( $minimumStepWidth === null ) ||
  150. ( $step->width < $minimumStepWidth ) )
  151. {
  152. $minimumStepWidth = $step->width;
  153. }
  154. }
  155. $width = abs(
  156. $axisBoundings->width * $minimumStepWidth * $this->direction->x +
  157. $axisBoundings->height * $minimumStepWidth * $this->direction->y
  158. );
  159. $height = abs(
  160. $ySpace * $this->direction->x +
  161. $xSpace * $this->direction->y
  162. );
  163. $length = sqrt( pow( $width, 2 ) + pow( $height, 2 ) );
  164. $this->angle = rad2deg( acos( $height / $length ) );
  165. }
  166. // Determine additional required axis space by boxes
  167. $firstStep = reset( $steps );
  168. $lastStep = end( $steps );
  169. $textAngle = $axisAngle +
  170. deg2rad( $this->angle ) +
  171. ( $axis->position & ( ezcGraph::TOP | ezcGraph::BOTTOM ) ? deg2rad( 270 ) : deg2rad( 90 ) );
  172. // Ensure angle between 0 and 360 degrees
  173. $degTextAngle = rad2deg( $textAngle );
  174. while ( $degTextAngle < 0 )
  175. {
  176. $degTextAngle += 360.;
  177. }
  178. if ( $this->properties['labelOffset'] )
  179. {
  180. $this->offset =
  181. ( $this->angle < 0 ? -1 : 1 ) *
  182. ( $axis->position & ( ezcGraph::TOP | ezcGraph::LEFT ) ? 1 : -1 ) *
  183. ( 1 - cos( deg2rad( $this->angle * 2 ) ) );
  184. }
  185. else
  186. {
  187. $this->offset = 0;
  188. }
  189. $axisSpaceFactor = abs(
  190. ( $this->direction->x == 0 ? 0 :
  191. $this->direction->x * $ySpace / $axisBoundings->width ) +
  192. ( $this->direction->y == 0 ? 0 :
  193. $this->direction->y * $xSpace / $axisBoundings->height )
  194. );
  195. $start = new ezcGraphCoordinate(
  196. $start->x + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ),
  197. $start->y + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y )
  198. );
  199. $end = new ezcGraphCoordinate(
  200. $end->x + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ),
  201. $end->y + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y )
  202. );
  203. $labelLength = sqrt(
  204. pow(
  205. $xSpace * $this->direction->y +
  206. $axisSpaceFactor * $this->offset * ( $end->x - $start->x ),
  207. 2 ) +
  208. pow(
  209. $ySpace * $this->direction->x +
  210. $axisSpaceFactor * $this->offset * ( $end->y - $start->y ),
  211. 2 )
  212. );
  213. $this->offset *= $axisSpaceFactor;
  214. // Draw steps and grid
  215. foreach ( $steps as $nr => $step )
  216. {
  217. $position = new ezcGraphCoordinate(
  218. $start->x + ( $end->x - $start->x ) * $step->position * abs( $this->direction->x ),
  219. $start->y + ( $end->y - $start->y ) * $step->position * abs( $this->direction->y )
  220. );
  221. $stepSize = new ezcGraphCoordinate(
  222. ( $end->x - $start->x ) * $step->width,
  223. ( $end->y - $start->y ) * $step->width
  224. );
  225. // Calculate label boundings
  226. switch ( true )
  227. {
  228. case ( $nr === 0 ):
  229. $labelSize = min(
  230. abs(
  231. $xSpace * 2 * $this->direction->y +
  232. $ySpace * 2 * $this->direction->x ),
  233. abs(
  234. $step->width * $axisBoundings->width * $this->direction->x +
  235. $step->width * $axisBoundings->height * $this->direction->y )
  236. );
  237. break;
  238. case ( $step->isLast ):
  239. $labelSize = min(
  240. abs(
  241. $xSpace * 2 * $this->direction->y +
  242. $ySpace * 2 * $this->direction->x ),
  243. abs(
  244. $steps[$nr - 1]->width * $axisBoundings->width * $this->direction->x +
  245. $steps[$nr - 1]->width * $axisBoundings->height * $this->direction->y )
  246. );
  247. break;
  248. default:
  249. $labelSize = abs(
  250. $step->width * $axisBoundings->width * $this->direction->x +
  251. $step->width * $axisBoundings->height * $this->direction->y
  252. );
  253. break;
  254. }
  255. $labelSize = $labelSize * cos( deg2rad( $this->angle ) );
  256. $lengthReducement = min(
  257. abs( tan( deg2rad( $this->angle ) ) * ( $labelSize / 2 ) ),
  258. abs( $labelLength / 2 )
  259. );
  260. switch ( true )
  261. {
  262. case ( ( ( $degTextAngle >= 0 ) &&
  263. ( $degTextAngle < 90 ) &&
  264. ( ( $axis->position === ezcGraph::LEFT ) ||
  265. ( $axis->position === ezcGraph::RIGHT )
  266. )
  267. ) ||
  268. ( ( $degTextAngle >= 270 ) &&
  269. ( $degTextAngle < 360 ) &&
  270. ( ( $axis->position === ezcGraph::TOP ) ||
  271. ( $axis->position === ezcGraph::BOTTOM )
  272. )
  273. )
  274. ):
  275. $labelBoundings = new ezcGraphBoundings(
  276. $position->x,
  277. $position->y,
  278. $position->x + abs( $labelLength ) - $lengthReducement,
  279. $position->y + $labelSize
  280. );
  281. $labelAlignement = ezcGraph::LEFT | ezcGraph::TOP;
  282. $labelRotation = $degTextAngle;
  283. break;
  284. case ( ( ( $degTextAngle >= 90 ) &&
  285. ( $degTextAngle < 180 ) &&
  286. ( ( $axis->position === ezcGraph::LEFT ) ||
  287. ( $axis->position === ezcGraph::RIGHT )
  288. )
  289. ) ||
  290. ( ( $degTextAngle >= 180 ) &&
  291. ( $degTextAngle < 270 ) &&
  292. ( ( $axis->position === ezcGraph::TOP ) ||
  293. ( $axis->position === ezcGraph::BOTTOM )
  294. )
  295. )
  296. ):
  297. $labelBoundings = new ezcGraphBoundings(
  298. $position->x - abs( $labelLength ) + $lengthReducement,
  299. $position->y,
  300. $position->x,
  301. $position->y + $labelSize
  302. );
  303. $labelAlignement = ezcGraph::RIGHT | ezcGraph::TOP;
  304. $labelRotation = $degTextAngle - 180;
  305. break;
  306. case ( ( ( $degTextAngle >= 180 ) &&
  307. ( $degTextAngle < 270 ) &&
  308. ( ( $axis->position === ezcGraph::LEFT ) ||
  309. ( $axis->position === ezcGraph::RIGHT )
  310. )
  311. ) ||
  312. ( ( $degTextAngle >= 90 ) &&
  313. ( $degTextAngle < 180 ) &&
  314. ( ( $axis->position === ezcGraph::TOP ) ||
  315. ( $axis->position === ezcGraph::BOTTOM )
  316. )
  317. )
  318. ):
  319. $labelBoundings = new ezcGraphBoundings(
  320. $position->x - abs( $labelLength ) + $lengthReducement,
  321. $position->y - $labelSize,
  322. $position->x,
  323. $position->y
  324. );
  325. $labelAlignement = ezcGraph::RIGHT | ezcGraph::BOTTOM;
  326. $labelRotation = $degTextAngle - 180;
  327. break;
  328. case ( ( ( $degTextAngle >= 270 ) &&
  329. ( $degTextAngle < 360 ) &&
  330. ( ( $axis->position === ezcGraph::LEFT ) ||
  331. ( $axis->position === ezcGraph::RIGHT )
  332. )
  333. ) ||
  334. ( ( $degTextAngle >= 0 ) &&
  335. ( $degTextAngle < 90 ) &&
  336. ( ( $axis->position === ezcGraph::TOP ) ||
  337. ( $axis->position === ezcGraph::BOTTOM )
  338. )
  339. )
  340. ):
  341. $labelBoundings = new ezcGraphBoundings(
  342. $position->x,
  343. $position->y + $labelSize,
  344. $position->x + abs( $labelLength ) - $lengthReducement,
  345. $position->y
  346. );
  347. $labelAlignement = ezcGraph::LEFT | ezcGraph::BOTTOM;
  348. $labelRotation = $degTextAngle;
  349. break;
  350. }
  351. $renderer->drawText(
  352. $labelBoundings,
  353. $step->label,
  354. $labelAlignement,
  355. new ezcGraphRotation(
  356. $labelRotation,
  357. $position
  358. )
  359. );
  360. // major grid
  361. if ( $axis->majorGrid )
  362. {
  363. $this->drawGrid(
  364. $renderer,
  365. $gridBoundings,
  366. $position,
  367. $stepSize,
  368. $axis->majorGrid
  369. );
  370. }
  371. // major step
  372. $this->drawStep(
  373. $renderer,
  374. $position,
  375. $this->direction,
  376. $axis->position,
  377. $this->majorStepSize,
  378. $axis->border
  379. );
  380. }
  381. }
  382. /**
  383. * Modify chart data position
  384. *
  385. * Optionally additionally modify the coodinate of a data point
  386. *
  387. * @param ezcGraphCoordinate $coordinate Data point coordinate
  388. * @return ezcGraphCoordinate Modified coordinate
  389. */
  390. public function modifyChartDataPosition( ezcGraphCoordinate $coordinate )
  391. {
  392. return new ezcGraphCoordinate(
  393. $coordinate->x * abs( $this->direction->y ) +
  394. ( $coordinate->x * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->x ),
  395. $coordinate->y * abs( $this->direction->x ) +
  396. ( $coordinate->y * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->y )
  397. );
  398. }
  399. }
  400. ?>