PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/fusenigk/mantisbt-1
PHP | 512 lines | 314 code | 39 blank | 159 comment | 21 complexity | 0362f3251e8794720665e5f44dd4ee90 MD5 | raw file
  1. <?php
  2. /**
  3. * File containing the ezcGraphAxisRotatedLabelRenderer class
  4. *
  5. * @package Graph
  6. * @version 1.5
  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.5
  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. * Determine label angle
  108. *
  109. * Determine the optiomal angle for the axis labels, of no angle has been
  110. * provided by the user.
  111. *
  112. * @param array $steps
  113. * @return void
  114. */
  115. protected function determineAngle( array $steps, $xSpace, $ySpace, ezcGraphBoundings $axisBoundings )
  116. {
  117. if ( $this->angle === null )
  118. {
  119. $minimumStepWidth = null;
  120. foreach ( $steps as $nr => $step )
  121. {
  122. if ( ( $minimumStepWidth === null ) ||
  123. ( $step->width < $minimumStepWidth ) )
  124. {
  125. $minimumStepWidth = $step->width;
  126. }
  127. }
  128. $width = abs(
  129. $axisBoundings->width * $minimumStepWidth * $this->direction->x +
  130. $axisBoundings->height * $minimumStepWidth * $this->direction->y
  131. );
  132. $height = abs(
  133. $ySpace * $this->direction->x +
  134. $xSpace * $this->direction->y
  135. );
  136. $length = sqrt( pow( $width, 2 ) + pow( $height, 2 ) );
  137. $this->angle = rad2deg( acos( $height / $length ) );
  138. }
  139. }
  140. /**
  141. * Determine text offset.
  142. *
  143. * Calculate the label offset and angle, from the configured or evaluated
  144. * text angle.
  145. *
  146. * Returns the text angle in degrees.
  147. *
  148. * @param ezcGraphChartElementAxis $axis
  149. * @param array $steps
  150. * @return float
  151. */
  152. protected function determineTextOffset( ezcGraphChartElementAxis $axis, array $steps ) {
  153. // Determine additional required axis space by boxes
  154. $firstStep = reset( $steps );
  155. $lastStep = end( $steps );
  156. $axisAngle = -$this->direction->angle( new ezcGraphVector( 1, 0 ) );
  157. $textAngle = $axisAngle +
  158. deg2rad( $this->angle ) +
  159. ( $axis->position & ( ezcGraph::TOP | ezcGraph::BOTTOM ) ? deg2rad( 270 ) : deg2rad( 90 ) );
  160. // Ensure angle between 0 and 360 degrees
  161. $degTextAngle = rad2deg( $textAngle );
  162. while ( $degTextAngle < 0 )
  163. {
  164. $degTextAngle += 360.;
  165. }
  166. if ( $this->properties['labelOffset'] )
  167. {
  168. $this->offset =
  169. ( $this->angle < 0 ? -1 : 1 ) *
  170. ( $axis->position & ( ezcGraph::TOP | ezcGraph::LEFT ) ? 1 : -1 ) *
  171. ( 1 - cos( deg2rad( $this->angle * 2 ) ) );
  172. }
  173. else
  174. {
  175. $this->offset = 0;
  176. }
  177. return $degTextAngle;
  178. }
  179. /**
  180. * Calculate label size
  181. *
  182. * Calculate the size of a single lable in a single step.
  183. *
  184. * @param array $steps
  185. * @param int $nr
  186. * @param array $step
  187. * @param float $xSpace
  188. * @param float $ySpace
  189. * @param ezcGraphBoundings $axisBoundings
  190. * @return float
  191. */
  192. protected function calculateLabelSize( array $steps, $nr, $step, $xSpace, $ySpace, ezcGraphBoundings $axisBoundings )
  193. {
  194. switch ( true )
  195. {
  196. case ( $nr === 0 ):
  197. $labelSize = min(
  198. abs(
  199. $xSpace * 2 * $this->direction->y +
  200. $ySpace * 2 * $this->direction->x ),
  201. abs(
  202. $step->width * $axisBoundings->width * $this->direction->x +
  203. $step->width * $axisBoundings->height * $this->direction->y )
  204. );
  205. break;
  206. case ( $step->isLast ):
  207. $labelSize = min(
  208. abs(
  209. $xSpace * 2 * $this->direction->y +
  210. $ySpace * 2 * $this->direction->x ),
  211. abs(
  212. $steps[$nr - 1]->width * $axisBoundings->width * $this->direction->x +
  213. $steps[$nr - 1]->width * $axisBoundings->height * $this->direction->y )
  214. );
  215. break;
  216. default:
  217. $labelSize = abs(
  218. $step->width * $axisBoundings->width * $this->direction->x +
  219. $step->width * $axisBoundings->height * $this->direction->y
  220. );
  221. break;
  222. }
  223. return $labelSize * cos( deg2rad( $this->angle ) );
  224. }
  225. /**
  226. * Calculate general label length
  227. *
  228. * @param ezcGraphCoordinate $start
  229. * @param ezcGraphCoordinate $end
  230. * @param float $xSpace
  231. * @param float $ySpace
  232. * @param ezcGraphBoundings $axisBoundings
  233. * @return float
  234. */
  235. protected function calculateLabelLength( ezcGraphCoordinate $start, ezcGraphCoordinate $end, $xSpace, $ySpace, ezcGraphBoundings $axisBoundings )
  236. {
  237. $axisSpaceFactor = abs(
  238. ( $this->direction->x == 0 ? 0 :
  239. $this->direction->x * $ySpace / $axisBoundings->width ) +
  240. ( $this->direction->y == 0 ? 0 :
  241. $this->direction->y * $xSpace / $axisBoundings->height )
  242. );
  243. $axisWidth = $end->x - $start->x;
  244. $axisHeight = $end->y - $start->y;
  245. $start->x += max( 0., $axisSpaceFactor * $this->offset ) * $axisWidth;
  246. $start->y += max( 0., $axisSpaceFactor * $this->offset ) * $axisHeight;
  247. $end->x += min( 0., $axisSpaceFactor * $this->offset ) * $axisWidth;
  248. $end->y += min( 0., $axisSpaceFactor * $this->offset ) * $axisHeight;
  249. $labelLength = sqrt(
  250. pow(
  251. $xSpace * $this->direction->y +
  252. ( $this->labelOffset ?
  253. $axisSpaceFactor * $this->offset * ( $end->x - $start->x ) :
  254. $ySpace * 2 * $this->direction->x
  255. ),
  256. 2 ) +
  257. pow(
  258. $ySpace * $this->direction->x +
  259. ( $this->labelOffset ?
  260. $axisSpaceFactor * $this->offset * ( $end->y - $start->y ) :
  261. $xSpace * 2 * $this->direction->y
  262. ),
  263. 2 )
  264. );
  265. $this->offset *= $axisSpaceFactor;
  266. return $labelLength;
  267. }
  268. /**
  269. * Render label text.
  270. *
  271. * Render the text of a single label, depending on the position, length and
  272. * rotation of the label.
  273. *
  274. * @param ezcGraphRenderer $renderer
  275. * @param ezcGraphChartElementAxis $axis
  276. * @param ezcGraphCoordinate $position
  277. * @param string $label
  278. * @param float $degTextAngle
  279. * @param float $labelLength
  280. * @param float $labelSize
  281. * @param float $lengthReducement
  282. * @return void
  283. */
  284. protected function renderLabelText( ezcGraphRenderer $renderer, ezcGraphChartElementAxis $axis, ezcGraphCoordinate $position, $label, $degTextAngle, $labelLength, $labelSize, $lengthReducement )
  285. {
  286. switch ( true )
  287. {
  288. case ( ( ( $degTextAngle >= 0 ) &&
  289. ( $degTextAngle < 90 ) &&
  290. ( ( $axis->position === ezcGraph::LEFT ) ||
  291. ( $axis->position === ezcGraph::RIGHT )
  292. )
  293. ) ||
  294. ( ( $degTextAngle >= 270 ) &&
  295. ( $degTextAngle < 360 ) &&
  296. ( ( $axis->position === ezcGraph::TOP ) ||
  297. ( $axis->position === ezcGraph::BOTTOM )
  298. )
  299. )
  300. ):
  301. $labelBoundings = new ezcGraphBoundings(
  302. $position->x,
  303. $position->y,
  304. $position->x + abs( $labelLength ) - $lengthReducement,
  305. $position->y + $labelSize
  306. );
  307. $labelAlignement = ezcGraph::LEFT | ezcGraph::TOP;
  308. $labelRotation = $degTextAngle;
  309. break;
  310. case ( ( ( $degTextAngle >= 90 ) &&
  311. ( $degTextAngle < 180 ) &&
  312. ( ( $axis->position === ezcGraph::LEFT ) ||
  313. ( $axis->position === ezcGraph::RIGHT )
  314. )
  315. ) ||
  316. ( ( $degTextAngle >= 180 ) &&
  317. ( $degTextAngle < 270 ) &&
  318. ( ( $axis->position === ezcGraph::TOP ) ||
  319. ( $axis->position === ezcGraph::BOTTOM )
  320. )
  321. )
  322. ):
  323. $labelBoundings = new ezcGraphBoundings(
  324. $position->x - abs( $labelLength ) + $lengthReducement,
  325. $position->y,
  326. $position->x,
  327. $position->y + $labelSize
  328. );
  329. $labelAlignement = ezcGraph::RIGHT | ezcGraph::TOP;
  330. $labelRotation = $degTextAngle - 180;
  331. break;
  332. case ( ( ( $degTextAngle >= 180 ) &&
  333. ( $degTextAngle < 270 ) &&
  334. ( ( $axis->position === ezcGraph::LEFT ) ||
  335. ( $axis->position === ezcGraph::RIGHT )
  336. )
  337. ) ||
  338. ( ( $degTextAngle >= 90 ) &&
  339. ( $degTextAngle < 180 ) &&
  340. ( ( $axis->position === ezcGraph::TOP ) ||
  341. ( $axis->position === ezcGraph::BOTTOM )
  342. )
  343. )
  344. ):
  345. $labelBoundings = new ezcGraphBoundings(
  346. $position->x - abs( $labelLength ) + $lengthReducement,
  347. $position->y - $labelSize,
  348. $position->x,
  349. $position->y
  350. );
  351. $labelAlignement = ezcGraph::RIGHT | ezcGraph::BOTTOM;
  352. $labelRotation = $degTextAngle - 180;
  353. break;
  354. case ( ( ( $degTextAngle >= 270 ) &&
  355. ( $degTextAngle < 360 ) &&
  356. ( ( $axis->position === ezcGraph::LEFT ) ||
  357. ( $axis->position === ezcGraph::RIGHT )
  358. )
  359. ) ||
  360. ( ( $degTextAngle >= 0 ) &&
  361. ( $degTextAngle < 90 ) &&
  362. ( ( $axis->position === ezcGraph::TOP ) ||
  363. ( $axis->position === ezcGraph::BOTTOM )
  364. )
  365. )
  366. ):
  367. $labelBoundings = new ezcGraphBoundings(
  368. $position->x,
  369. $position->y,
  370. $position->x - abs( $labelLength ) + $lengthReducement,
  371. $position->y + $labelSize
  372. );
  373. $labelAlignement = ezcGraph::LEFT | ezcGraph::TOP;
  374. $labelRotation = $degTextAngle;
  375. break;
  376. }
  377. $renderer->drawText(
  378. $labelBoundings,
  379. $label,
  380. $labelAlignement,
  381. new ezcGraphRotation(
  382. $labelRotation,
  383. $position
  384. )
  385. );
  386. }
  387. /**
  388. * Render Axis labels
  389. *
  390. * Render labels for an axis.
  391. *
  392. * @param ezcGraphRenderer $renderer Renderer used to draw the chart
  393. * @param ezcGraphBoundings $boundings Boundings of the axis
  394. * @param ezcGraphCoordinate $start Axis starting point
  395. * @param ezcGraphCoordinate $end Axis ending point
  396. * @param ezcGraphChartElementAxis $axis Axis instance
  397. * @return void
  398. */
  399. public function renderLabels(
  400. ezcGraphRenderer $renderer,
  401. ezcGraphBoundings $boundings,
  402. ezcGraphCoordinate $start,
  403. ezcGraphCoordinate $end,
  404. ezcGraphChartElementAxis $axis,
  405. ezcGraphBoundings $innerBoundings = null )
  406. {
  407. // receive rendering parameters from axis
  408. $steps = $axis->getSteps();
  409. $axisBoundings = new ezcGraphBoundings(
  410. $start->x, $start->y,
  411. $end->x, $end->y
  412. );
  413. // Determine normalized axis direction
  414. $this->direction = new ezcGraphVector(
  415. $end->x - $start->x,
  416. $end->y - $start->y
  417. );
  418. $this->direction->unify();
  419. // Get axis space
  420. $gridBoundings = null;
  421. list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings );
  422. // Determine optimal angle if none specified
  423. $this->determineAngle( $steps, $xSpace, $ySpace, $axisBoundings );
  424. $degTextAngle = $this->determineTextOffset( $axis, $steps );
  425. $labelLength = $this->calculateLabelLength( $start, $end, $xSpace, $ySpace, $axisBoundings );
  426. // Draw steps and grid
  427. foreach ( $steps as $nr => $step )
  428. {
  429. $position = new ezcGraphCoordinate(
  430. $start->x + ( $end->x - $start->x ) * $step->position * abs( $this->direction->x ),
  431. $start->y + ( $end->y - $start->y ) * $step->position * abs( $this->direction->y )
  432. );
  433. $stepSize = new ezcGraphCoordinate(
  434. ( $end->x - $start->x ) * $step->width,
  435. ( $end->y - $start->y ) * $step->width
  436. );
  437. // Calculate label boundings
  438. $labelSize = $this->calculateLabelSize( $steps, $nr, $step, $xSpace, $ySpace, $axisBoundings );
  439. $lengthReducement = min(
  440. abs( tan( deg2rad( $this->angle ) ) * ( $labelSize / 2 ) ),
  441. abs( $labelLength / 2 )
  442. );
  443. $this->renderLabelText( $renderer, $axis, $position, $step->label, $degTextAngle, $labelLength, $labelSize, $lengthReducement );
  444. // Major grid
  445. if ( $axis->majorGrid )
  446. {
  447. $this->drawGrid( $renderer, $gridBoundings, $position, $stepSize, $axis->majorGrid );
  448. }
  449. // Major step
  450. $this->drawStep( $renderer, $position, $this->direction, $axis->position, $this->majorStepSize, $axis->border );
  451. }
  452. }
  453. /**
  454. * Modify chart data position
  455. *
  456. * Optionally additionally modify the coodinate of a data point
  457. *
  458. * @param ezcGraphCoordinate $coordinate Data point coordinate
  459. * @return ezcGraphCoordinate Modified coordinate
  460. */
  461. public function modifyChartDataPosition( ezcGraphCoordinate $coordinate )
  462. {
  463. return new ezcGraphCoordinate(
  464. $coordinate->x * abs( $this->direction->y ) +
  465. ( $coordinate->x * ( 1 - abs( $this->offset ) ) * abs( $this->direction->x ) ) +
  466. ( abs( $this->offset ) * abs( $this->direction->x ) ),
  467. $coordinate->y * abs( $this->direction->x ) +
  468. ( $coordinate->y * ( 1 - abs( $this->offset ) ) * abs( $this->direction->y ) ) +
  469. ( abs( $this->offset ) * abs( $this->direction->y ) )
  470. );
  471. }
  472. }
  473. ?>