diff --git a/library/Icinga/Chart/Axis.php b/library/Icinga/Chart/Axis.php index 00a5f5679..74c40b495 100644 --- a/library/Icinga/Chart/Axis.php +++ b/library/Icinga/Chart/Axis.php @@ -77,6 +77,20 @@ class Axis implements Drawable */ private $yUnit = null; + /** + * The minimum amount of units each step must take up + * + * @var int + */ + public $minUnitsPerStep = 80; + + /** + * The minimum amount of units each tick must take up + * + * @var int + */ + public $minUnitsPerTick = 15; + /** * If the displayed labels should be aligned horizontally or diagonally */ @@ -160,6 +174,14 @@ class Axis implements Drawable */ private function renderHorizontalAxis(RenderContext $ctx, DOMElement $group) { + $steps = $this->ticksPerX($this->xUnit->getTicks(), $ctx->getNrOfUnitsX(), $this->minUnitsPerStep); + $ticks = $this->ticksPerX($this->xUnit->getTicks(), $ctx->getNrOfUnitsX(), $this->minUnitsPerTick); + + // Steps should always be ticks + if ($ticks !== $steps) { + $steps = $ticks * 5; + } + $line = new Line(0, 100, 100, 100); $line->setStrokeWidth(2); $group->appendChild($line->toSvg($ctx)); @@ -168,41 +190,48 @@ class Axis implements Drawable $lastLabelEnd = -1; $shift = 0; + $i = 0; foreach ($this->xUnit as $label => $pos) { - if ($this->labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) { - // If the last label would overlap this label we shift the y axis a bit - if ($lastLabelEnd > $pos) { - $shift = ($shift + 5) % 10; - } else { - $shift = 0; + + if ($i % $ticks === 0) { + $tick = new Line($pos, 100, $pos, 101); + $group->appendChild($tick->toSvg($ctx)); + } + + if ($i % $steps === 0) { + if ($this->labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) { + // If the last label would overlap this label we shift the y axis a bit + if ($lastLabelEnd > $pos) { + $shift = ($shift + 5) % 10; + } else { + $shift = 0; + } } + + $labelField = new Text($pos + 0.5, ($this->xLabel ? 107 : 105) + $shift, $label); + if ($this->labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) { + $labelField->setAlignment(Text::ALIGN_MIDDLE) + ->setFontSize('1.8em'); + } else { + $labelField->setFontSize('2.5em'); + } + + if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) { + $labelField = new Rotator($labelField, 45); + } + $labelField = $labelField->toSvg($ctx); + + $group->appendChild($labelField); + + if ($this->drawYGrid) { + $bgLine = new Line($pos, 0, $pos, 100); + $bgLine->setStrokeWidth(0.5) + ->setStrokeColor('#232'); + $group->appendChild($bgLine->toSvg($ctx)); + } + $lastLabelEnd = $pos + strlen($label) * 1.2; } - - $tick = new Line($pos, 100, $pos, 102); - $group->appendChild($tick->toSvg($ctx)); - - $labelField = new Text($pos + 0.5, ($this->xLabel ? 107 : 105) + $shift, $label); - if ($this->labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) { - $labelField->setAlignment(Text::ALIGN_MIDDLE) - ->setFontSize('1.8em'); - } else { - $labelField->setFontSize('2.5em'); - } - - if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) { - $labelField = new Rotator($labelField, 45); - } - $labelField = $labelField->toSvg($ctx); - - $group->appendChild($labelField); - - if ($this->drawYGrid) { - $bgLine = new Line($pos, 0, $pos, 100); - $bgLine->setStrokeWidth(0.5) - ->setStrokeColor('#232'); - $group->appendChild($bgLine->toSvg($ctx)); - } - $lastLabelEnd = $pos + strlen($label) * 1.2; + $i++; } // render the label for this axis @@ -223,26 +252,42 @@ class Axis implements Drawable */ private function renderVerticalAxis(RenderContext $ctx, DOMElement $group) { + $steps = $this->ticksPerX($this->yUnit->getTicks(), $ctx->getNrOfUnitsY(), $this->minUnitsPerStep); + $ticks = $this->ticksPerX($this->yUnit->getTicks(), $ctx->getNrOfUnitsY(), $this->minUnitsPerTick); + + // Steps should always be ticks + if ($ticks !== $steps) { + $steps = $ticks * 5; + } $line = new Line(0, 0, 0, 100); $line->setStrokeWidth(2); $group->appendChild($line->toSvg($ctx)); + $i = 0; foreach ($this->yUnit as $label => $pos) { $pos = 100 - $pos; - $tick = new Line(0, $pos, -1, $pos); - $group->appendChild($tick->toSvg($ctx)); - $labelField = new Text(-0.5, $pos+0.5, $label); - $labelField->setFontSize('1.8em') - ->setAlignment(Text::ALIGN_END); - - $group->appendChild($labelField->toSvg($ctx)); - if ($this->drawXGrid) { - $bgLine = new Line(0, $pos, 100, $pos); - $bgLine->setStrokeWidth(0.5) - ->setStrokeColor('#343'); - $group->appendChild($bgLine->toSvg($ctx)); + if ($i % $ticks === 0) { + // draw a tick + $tick = new Line(0, $pos, -1, $pos); + $group->appendChild($tick->toSvg($ctx)); } + + if ($i % $steps === 0) { + // draw a step + $labelField = new Text(-0.5, $pos+0.5, $label); + $labelField->setFontSize('1.8em') + ->setAlignment(Text::ALIGN_END); + + $group->appendChild($labelField->toSvg($ctx)); + if ($this->drawXGrid) { + $bgLine = new Line(0, $pos, 100, $pos); + $bgLine->setStrokeWidth(0.5) + ->setStrokeColor('#343'); + $group->appendChild($bgLine->toSvg($ctx)); + } + } + $i++; } if ($this->yLabel) { @@ -416,4 +461,14 @@ class Axis implements Drawable $this->renderVerticalAxis($ctx, $group); return $group; } + + protected function ticksPerX($ticks, $units, $min) + { + $per = 1; + while ($per * $units / $ticks < $min) { + $per++; + } + return $per; + } + } diff --git a/library/Icinga/Chart/Graph/BarGraph.php b/library/Icinga/Chart/Graph/BarGraph.php index 1225307ad..0ec5e77a9 100644 --- a/library/Icinga/Chart/Graph/BarGraph.php +++ b/library/Icinga/Chart/Graph/BarGraph.php @@ -28,7 +28,7 @@ class BarGraph extends Styleable implements Drawable * * @var int */ - private $barWidth = 4; + private $barWidth = 1; /** * The dataset to use for this bar graph diff --git a/library/Icinga/Chart/Unit/AxisUnit.php b/library/Icinga/Chart/Unit/AxisUnit.php index dc57f99e0..fba521778 100644 --- a/library/Icinga/Chart/Unit/AxisUnit.php +++ b/library/Icinga/Chart/Unit/AxisUnit.php @@ -9,13 +9,14 @@ use Iterator; /** * Base class for Axis Units * + * An AxisUnit takes a set of values and places them on a given range + * * Concrete subclasses must implement the iterator interface, with * getCurrent returning the axis relative position and getValue the label * that will be displayed */ interface AxisUnit extends Iterator { - /** * Add a dataset to this AxisUnit, required for dynamic min and max vlaues * @@ -46,4 +47,11 @@ interface AxisUnit extends Iterator * @param int $max The new maximum value */ public function setMax($max); + + /** + * Get the amount of ticks of this axis + * + * @return int + */ + public function getTicks(); } diff --git a/library/Icinga/Chart/Unit/LinearUnit.php b/library/Icinga/Chart/Unit/LinearUnit.php index d776fe304..fe80971d4 100644 --- a/library/Icinga/Chart/Unit/LinearUnit.php +++ b/library/Icinga/Chart/Unit/LinearUnit.php @@ -9,7 +9,6 @@ namespace Icinga\Chart\Unit; */ class LinearUnit implements AxisUnit { - /** * The minimum value to display * @@ -43,7 +42,7 @@ class LinearUnit implements AxisUnit * * @var int */ - private $nrOfTicks = 10; + protected $nrOfTicks = 10; /** * The currently displayed tick @@ -95,45 +94,13 @@ class LinearUnit implements AxisUnit if (!$this->staticMin) { $this->min = min($this->min, $datapoints[0]); } - if (!$this->staticMin || !$this->staticMax) { - $this->updateMaxValue(); - } $this->currentTick = 0; $this->currentValue = $this->min; - return $this; - } - - /** - * Refresh the range depending on the current values of min, max and nrOfTicks - */ - private function updateMaxValue() - { - $this->max = $this->calculateTickRange($this->max - $this->min, $this->nrOfTicks) * - $this->nrOfTicks + $this->min; - } - - /** - * Determine the minimum tick range that is necessary to display the given value range - * correctly - * - * @param int range The range to display - * @param int ticks The amount of ticks to use - * - * @return int The value for each tick - */ - private function calculateTickRange($range, $ticks) - { - $factor = 1; - $steps = array(1, 2, 5); - $step = 0; - while ($range / ($factor * $steps[$step]) > $ticks) { - $step++; - if ($step === count($steps)) { - $step = 0; - $factor *= 10; - } + if ($this->max === $this->min) { + $this->max = $this->min + 10; } - return $steps[$step] * $factor; + $this->nrOfTicks = $this->max - $this->min; + return $this; } /** @@ -149,7 +116,7 @@ class LinearUnit implements AxisUnit } elseif ($value > $this->max) { return 100; } else { - return 100 * ($value - $this->min) / $this->max - $this->min; + return 100 * ($value - $this->min) / $this->nrOfTicks; } } @@ -211,7 +178,6 @@ class LinearUnit implements AxisUnit if ($max !== null) { $this->max = $max; $this->staticMax = true; - $this->updateMaxValue(); } } @@ -225,7 +191,6 @@ class LinearUnit implements AxisUnit if ($min !== null) { $this->min = $min; $this->staticMin = true; - $this->updateMaxValue(); } } @@ -248,4 +213,14 @@ class LinearUnit implements AxisUnit { return $this->max; } + + /** + * Get the amount of ticks necessary to display this AxisUnit + * + * @return int + */ + public function getTicks() + { + return $this->nrOfTicks; + } } diff --git a/library/Icinga/Chart/Unit/LogarithmicUnit.php b/library/Icinga/Chart/Unit/LogarithmicUnit.php index 44580b15e..6d07f3d45 100644 --- a/library/Icinga/Chart/Unit/LogarithmicUnit.php +++ b/library/Icinga/Chart/Unit/LogarithmicUnit.php @@ -134,7 +134,7 @@ class LogarithmicUnit implements AxisUnit { $currentBase = $this->currentTick + $this->minExp; if (abs($currentBase) > 4) { - return '10E' . $currentBase; + return $this->base . 'E' . $currentBase; } return (string) intval($this->pow($currentBase)); } @@ -257,7 +257,7 @@ class LogarithmicUnit implements AxisUnit * * @return int */ - protected function getTicks() + public function getTicks() { return $this->maxExp - $this->minExp; } diff --git a/library/Icinga/Chart/Unit/StaticAxis.php b/library/Icinga/Chart/Unit/StaticAxis.php index 6458ae599..c7e9c2be5 100644 --- a/library/Icinga/Chart/Unit/StaticAxis.php +++ b/library/Icinga/Chart/Unit/StaticAxis.php @@ -118,4 +118,14 @@ class StaticAxis implements AxisUnit { return reset($this->items); } + + /** + * Get the amount of ticks of this axis + * + * @return int + */ + public function getTicks() + { + return count($this->items); + } }