icingaweb2/library/Icinga/Chart/Primitive/PieSlice.php

311 lines
9.1 KiB
PHP

<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Primitive;
use \DOMElement;
use \Icinga\Chart\Render\RenderContext;
/**
* Component for drawing a pie slice
*/
class PieSlice extends Animatable implements Drawable
{
/**
* The radius of this pieslice relative to the canvas
*
* @var int
*/
private $radius = 50;
/**
* The start radian of the pie slice
*
* @var float
*/
private $startRadian = 0;
/**
* The end radian of the pie slice
*
* @var float
*/
private $endRadian= 0;
/**
* The x position of the pie slice's center
*
* @var int
*/
private $x;
/**
* The y position of the pie slice's center
*
* @var int
*/
private $y;
/**
* The caption of the pie slice, empty string means no caption
*
* @var string
*/
private $caption = "";
/**
* The offset of the caption, shifting the indicator from the center of the pie slice
*
* This is required for nested pie slices.
*
* @var int
*/
private $captionOffset = 0;
/**
* The minimum radius the label must respect
*
* @var int
*/
private $outerCaptionBound = 0;
/**
* An optional group element to add labels to when rendering
*
* @var DOMElement
*/
private $labelGroup;
/**
* Create a pie slice
*
* @param int $radius The radius of the slice
* @param int $percent The percentage the slice represents
* @param int $percentStart The percentage where this slice starts
*/
public function __construct($radius, $percent, $percentStart = 0)
{
$this->x = $this->y = $this->radius = $radius;
$this->startRadian = M_PI * $percentStart/50;
$this->endRadian = M_PI * ($percent + $percentStart)/50;
}
/**
* Create the path for the pie slice
*
* @param int $x The x position of the pie slice
* @param int $y The y position of the pie slice
* @param int $r The absolute radius of the pie slice
*
* @return string A SVG path string
*/
private function getPieSlicePath($x, $y, $r)
{
// start at the center of the pieslice
$pathString = 'M ' . $x . ' ' . $y . ' ';
// The coordinate system is mirrored on the Y axis, so we have to flip cos and sin
$xStart = $x + intval($r * sin($this->startRadian));
$yStart = $y - intval($r * cos($this->startRadian));
$xEnd = $x + intval($r * sin($this->endRadian));
$yEnd = $y - intval($r * cos($this->endRadian));
// Draw a straight line to the upper part of the arc
$pathString .= 'L ' . $xStart . ' ' . $yStart;
// Instead of directly connecting the upper part of the arc (leaving a triangle), draw a bow with the radius
$pathString .= ' A ' . $r . ' ' . $r ;
// These are the flags for the bow, see the SVG path documentation for details
// http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
$pathString .= ' 0 ' . (($this->endRadian - $this->startRadian > M_PI) ? '1' : '0 ') . ' 1';
// xEnd and yEnd are the lower point of the arc
$pathString .= ' '.$xEnd . ' ' . $yEnd;
return $pathString;
}
/**
* Draw the label handler and the text for this pie slice
*
* @param RenderContext $ctx The rendering context to use for coordinate translation
* @param int $r The radius of the pie in absolute coordinates
*
* @return DOMElement The group DOMElement containing the handle and label
*/
private function drawDescriptionLabel(RenderContext $ctx, $r)
{
$group = $ctx->getDocument()->createElement('g');
$rOuter = ($ctx->xToAbsolute($this->outerCaptionBound) + $ctx->yToAbsolute($this->outerCaptionBound)) / 2;
$addOffset = $rOuter - $r ;
if ($addOffset < 0) {
$addOffset = 0;
}
list($x, $y) = $ctx->toAbsolute($this->x, $this->y);
$midRadius = $this->startRadian + ($this->endRadian - $this->startRadian) / 2;
list($offsetX, $offsetY) = $ctx->toAbsolute($this->captionOffset, $this->captionOffset);
$midX = $x + intval(($offsetX + $r)/2 * sin($midRadius));
$midY = $y - intval(($offsetY + $r)/2 * cos($midRadius));
// Draw the handle
$path = new Path(array($midX, $midY));
$midX += ($addOffset + $r/1.8) * ($midRadius > M_PI ? -1 : 1);
$path->append(array($midX, $midY))->toAbsolute();
$midX += intval($r/2 * sin(M_PI/7)) * ($midRadius > M_PI ? -1 : 1);
$midY -= intval($r/2 * cos(M_PI/7)) * ($midRadius < M_PI*1.5 && $midRadius > M_PI/2 ? -1 : 1);
if ($ctx->ytoRelative($midY) > 100) {
$midY = $ctx->yToAbsolute(100);
} elseif ($ctx->ytoRelative($midY) < 0) {
$midY = $ctx->yToAbsolute(0);
}
$path->append(array($midX , $midY));
$rel = $ctx->toRelative($midX, $midY);
// Draw the text box
$text = new Text($rel[0]+1.5, $rel[1], $this->caption);
$text->setFontSize('2.5em');
$text->setAlignment(($midRadius > M_PI ? Text::ALIGN_END : Text::ALIGN_START));
$group->appendChild($path->toSvg($ctx));
$group->appendChild($text->toSvg($ctx));
return $group;
}
/**
* Set the x position of the pie slice
*
* @param int $x The new x position
*
* @return self Fluid interface
*/
public function setX($x)
{
$this->x = $x;
return $this;
}
/**
* Set the y position of the pie slice
*
* @param int $y The new y position
*
* @return self Fluid interface
*/
public function setY($y)
{
$this->y = $y;
return $this;
}
/**
* Set a root element to be used for drawing labels
*
* @param DOMElement $group The label group
*
* @return self Fluid interface
*/
public function setLabelGroup(DOMElement $group)
{
$this->labelGroup = $group;
return $this;
}
/**
* Set the caption for this label
*
* @param string $caption The caption for this element
*
* @return self Fluid interface
*/
public function setCaption($caption)
{
$this->caption = $caption;
return $this;
}
/**
* Set the internal offset of the caption handle
*
* @param int $offset The offset for the caption handle
*
* @return self Fluid interface
*/
public function setCaptionOffset($offset)
{
$this->captionOffset = $offset;
return $this;
}
/**
* Set the minimum radius to be used for drawing labels
*
* @param int $bound The offset for the caption text
*
* @return self Fluid interface
*/
public function setOuterCaptionBound($bound)
{
$this->outerCaptionBound = $bound;
return $this;
}
/**
* Create the SVG representation from this Drawable
*
* @param RenderContext $ctx The context to use for rendering
*
* @return DOMElement The SVG Element
*/
public function toSvg(RenderContext $ctx)
{
$doc = $ctx->getDocument();
$group = $doc->createElement('g');
$r = ($ctx->xToAbsolute($this->radius) + $ctx->yToAbsolute($this->radius)) / 2;
list($x, $y) = $ctx->toAbsolute($this->x, $this->y);
$slicePath = $doc->createElement('path');
$slicePath->setAttribute('d', $this->getPieSlicePath($x, $y, $r));
$slicePath->setAttribute('style', $this->getStyle());
$slicePath->setAttribute('data-icinga-graph-type', 'pieslice');
$this->applyAttributes($slicePath);
$group->appendChild($slicePath);
if ($this->caption != "") {
$lblGroup = ($this->labelGroup ? $this->labelGroup : $group);
$lblGroup->appendChild($this->drawDescriptionLabel($ctx, $r));
}
return $group;
}
}