Matthias Jentsch 2360f21b09 Fix InlinePie boundaries and use floats for the PieSlice path coordinates
Use floats as path coordinates in PieSlices, to
avoid that the start and ending position of the arc are at the exact same
position. This would cause buggy behavior, when displaying values like "99,999%".

refs #5863
2014-03-26 17:06:21 +01:00

312 lines
9.1 KiB
PHP

<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - 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 + ($r * sin($this->startRadian));
$yStart = $y - ($r * cos($this->startRadian));
$xEnd = $x + ($r * sin($this->endRadian));
$yEnd = $y - ($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/9)) * ($midRadius > M_PI ? -1 : 1);
$midY -= intval($r/2 * cos(M_PI/3)) * ($midRadius < M_PI*1.4 && $midRadius > M_PI/3 ? -1 : 1);
if ($ctx->ytoRelative($midY) > 100) {
$midY = $ctx->yToAbsolute(100);
} elseif ($ctx->ytoRelative($midY) < 0) {
$midY = $ctx->yToAbsolute($ctx->ytoRelative(100+$midY));
}
$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;
}
}