. */ /** * For more information, please contact */ namespace Goat1000\SVGGraph; /** * An axis with multiple levels of divisions */ class DisplayAxisLevels extends DisplayAxis { protected $level_count; protected $levels; /** * Pass all these to parent constructor */ public function __construct(&$graph, &$axis, $axis_no, $orientation, $type, $main, $label_centre) { parent::__construct($graph, $axis, $axis_no, $orientation, $type, $main, $label_centre); if(!$this->show_text) { $this->levels = [null]; $this->level_count = 1; return; } $levels = $graph->getOption(['axis_levels_' . $orientation, $axis_no]); if(!is_array($levels)) $levels = array_fill(0, $levels, null); $this->levels = $levels; $this->level_count = count($levels); } /** * Returns the extents of the axis, relative to where it will be drawn from * returns BoundingBox */ public function measure($with_label = true) { if($this->level_count <= 1) return parent::measure($with_label); $bbox = new BoundingBox(0, 0, 0, 0); for($i = 0; $i < $this->level_count; ++$i) { $dbox = $this->getDivisionsBBox($i); $tbox = $this->getTextBBox($i); if($this->orientation == 'h') { $dbox->flipY(); if($this->axis_no > 0) { $dbox->offset(0, $bbox->y1); $tbox->offset(0, $bbox->y1); } else { $dbox->offset(0, $bbox->y2); $tbox->offset(0, $bbox->y2); } } else { if($this->axis_no > 0) { $dbox->offset($bbox->x2, 0); $tbox->offset($bbox->x2, 0); } else { $dbox->offset($bbox->x1, 0); $tbox->offset($bbox->x1, 0); } } // store the text box for this level $this->levels[$i]['text_box'] = $tbox; $bbox->growBox($tbox); $bbox->growBox($dbox); } if($with_label && $this->show_label) { $lpos = $this->getLabelPosition(); $bbox->grow($lpos['x'], $lpos['y'], $lpos['x'] + $lpos['width'], $lpos['y'] + $lpos['height']); } $bbox = $this->addOffset($bbox); return $bbox; } /** * Returns the bounding box of the text */ protected function getTextBBox($level) { if($this->level_count <= 1 || !$this->boxed_text) return parent::getTextBBox($level); $bbox = new BoundingBox(0, 0, 0, 0); list($x_off, $y_off, $opp) = $this->getTextOffset(0, 0, 0, 0, 0, 0, $level); $t_offset = ($this->orientation == 'h' ? $x_off : $y_off); if($this->axis->reversed()) $t_offset = -$t_offset; $length = $this->axis->getLength(); $points = $this->axis->getGridPoints(0); $lpoints = $this->getPointsForLevel($points, $level); $positions = $this->getTextPositions(0, 0, $x_off, $y_off, $lpoints, null); $pcount = count($positions); for($p = 0; $p < $pcount; ++$p) { $point = $lpoints[$p]; if(!$this->pointTextVisible($point, $length, $t_offset)) continue; if(!$point->blank($level)) { $pos = $positions[$p]; $lbl = $this->measureText($pos['x'], $pos['y'], $point, $opp, $level); $bbox->grow($lbl['x'], $lbl['y'], $lbl['x'] + $lbl['width'], $lbl['y'] + $lbl['height']); } } return $bbox; } /** * Draws the axis text labels */ public function drawText($x, $y, $gx, $gy, $g_width, $g_height) { if($this->level_count <= 1) return parent::drawText($x, $y, $gx, $gy, $g_width, $g_height); list($x_offset, $y_offset, $opposite) = $this->getTextOffset($x, $y, $gx, $gy, $g_width, $g_height, 0); $t_offset = ($this->orientation == 'h' ? $x_offset : $y_offset); if($this->axis->reversed()) $t_offset = -$t_offset; // measure function fills in text locations $this->measure(); $labels = ''; $anchor = null; $points = $this->axis->getGridPoints(0); $length = $this->axis->getLength(); for($i = 0; $i < $this->level_count; ++$i) { $count = count($points); if($this->block_label) --$count; list($x_offset, $y_offset, $opposite) = $this->getTextOffset($x, $y, $gx, $gy, $g_width, $g_height, $i); $x1 = $x_offset; $y1 = $y_offset; $tbox = $this->levels[$i]['text_box']; if($this->orientation == 'h') { if($this->axis_no > 0) $y1 = $y_offset + $tbox->y2; else $y1 = $y_offset + $tbox->y1; switch($this->styles['t_align']) { case 'left': $anchor = 'start'; break; case 'right': $anchor = 'end'; break; } } else { if($this->axis_no > 0) $x1 = $x_offset + $tbox->x1; else $x1 = $x_offset + $tbox->x2; switch($this->styles['t_align']) { case 'left': if(!$opposite) { $anchor = 'start'; $x1 = $tbox->x1; } break; case 'centre': $x1 = ($tbox->x1 + $tbox->x2) / 2; $anchor = 'middle'; break; } } $lpoints = $this->getPointsForLevel($points, $i); $positions = $this->getTextPositions($x, $y, $x1, $y1, $lpoints, $anchor); $pcount = count($positions); for($p = 0; $p < $pcount; ++$p) { $point = $lpoints[$p]; if(!$this->pointTextVisible($point, $length, $t_offset)) continue; $pos = $positions[$p]; $labels .= $this->getText($pos['x'], $pos['y'], $point, $opposite, $i, $anchor); } } if($labels != '') { $group = [ 'font-family' => $this->styles['t_font'], 'font-size' => $this->styles['t_font_size'], 'fill' => $this->styles['t_colour'], ]; $weight = $this->styles['t_font_weight']; if($weight != 'normal' && $weight !== null) $group['font-weight'] = $weight; $labels = $this->graph->element('g', $group, null, $labels); } if($this->show_label) $labels .= $this->getLabel($x, $y, $gx, $gy, $g_width, $g_height); return $labels; } /** * Draws the axis divisions */ public function drawDivisions($x, $y, $g_width, $g_height) { // parent can do simple divisions if($this->level_count <= 1 || !$this->boxed_text) return parent::drawDivisions($x, $y, $g_width, $g_height); $path = ''; $points = $this->axis->getGridPoints(0); $x_offset = $y_offset = 0; // direction of offset depends on h/v and axis number $direction = ($this->orientation == 'v' ? -1 : 1); if($this->axis_no > 0) $direction = -$direction; for($i = 0; $i < $this->level_count; ++$i) { $path_info = $this->getDivisionPathInfo(false, $g_width, $g_height, $i); if($path_info === null) continue; $lpoints = $this->getPointsForLevel($points, $i); $path .= $this->getDivisionPath($x + $x_offset, $y + $y_offset, $lpoints, $path_info, $i); if($this->orientation == 'h') $y_offset += $direction * $path_info['sz']; else $x_offset += $direction * $path_info['sz']; } if($path == '') return ''; $attr = [ 'd' => $path, 'stroke' => $this->styles['d_colour'], 'fill' => 'none', ]; return $this->graph->element('path', $attr); } /** * Returns the list of points with text for $level */ protected function getPointsForLevel($points, $level) { // need first point even if it is blank $lpoints = [$points[0]]; $prev = $points[0]->getText($level); $last_point = count($points) - 1; $end = $this->boxed_text ? $last_point : $last_point + 1; for($p = 1; $p < $end; ++$p) { if($points[$p]->blank($level)) continue; $label = $points[$p]->getText($level); if($label == $prev) continue; $lpoints[] = $points[$p]; $prev = $label; } // the last point is the division at the end of the axis if($this->boxed_text) $lpoints[] = $points[$last_point]; return $lpoints; } }