From bbe218d7fe5fe3fcf5a1097b4cb871b795efe581 Mon Sep 17 00:00:00 2001 From: Joshua Seigler Date: Mon, 4 Jul 2016 03:10:54 -0400 Subject: [PATCH] improved demo, added "honest charts" zero-axis option --- demo.php | 17 ++++----- src/NeatCharts/LineChart.php | 69 +++++++++++++++++++----------------- src/NeatCharts/NeatChart.php | 32 +++++++++++++---- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/demo.php b/demo.php index c15afc9..004a5e1 100644 --- a/demo.php +++ b/demo.php @@ -3,9 +3,9 @@ require_once 'vendor/autoload.php'; function randomData($count = 96) { $randomData = []; - $offset = 100 * (rand()/getRandMax())**4; - $scale = 100 * (rand()/getRandMax())**2; - $volatility = 0.5 * (rand()/getRandMax())**3; + $offset = 100 * (rand()/getRandMax())**2; + $scale = max(0.1 * $offset, 100 * rand() / getRandMax()); + $volatility = 0.25 * (rand()/getRandMax())**3 + 0.25; for ($n = 0, $current = $offset + 0.5 * $scale; $n < $count; $n++) { $current -= $offset; $current *= 1 + $volatility * (rand()/getRandMax() - 0.5); @@ -32,14 +32,14 @@ function randomData($count = 96) {
-

SVG chart in img tag

+

Chart in img tag

Random generated data, loaded as an image
-

SVG chart in svg tag

+

Chart in svg tag, zero axis shown

'#F00', 'labelColor'=>'#222', 'smoothed'=>false, - 'fontSize'=>14 + 'fontSize'=>14, + 'yAxisZero'=>true ]); echo $chart->render(); ?> @@ -56,7 +57,7 @@ echo $chart->render();
-

Smoothed SVG chart in svg tag

+

Smoothed chart in svg tag

render();
-

SVG sparkline in svg tag

+

Sparkline in svg tag

options = [ // LineChart defaults + 'width' => 800, + 'height' => 250, + 'lineColor' => '#000', + 'markerColor' => '#000', + 'labelColor' => '#000', + 'smoothed' => false, + 'fontSize' => 15, + 'yAxisEnabled'=>true, + 'xAxisEnabled'=>false, + 'yAxisZero'=>false + ]; + parent::setOptions($options); + } + public function setData($chartData) { + $this->setWindow($chartData, $this->options); // sets min, max, range, etc // we assume $chartData is sorted by key and keys and values are all numeric $previousX = $previousY = null; - end($chartData); - $this->xMax = key($chartData); - reset($chartData); - $this->xMin = key($chartData); - $this->xRange = $this->xMax - $this->xMin; $count = count($chartData); $deltaX = $this->xRange / $count; - $this->yMin = INF; // so the first comparison sets this to an actual value - $this->yMax = -INF; $averageAbsSlope = 0; // we will add all of them then divide to get an average $secants = []; // slope between this point and the previous one $tangents = []; // slope across the point foreach ($chartData as $x => $y) { - if ($y < $this->yMin) { - $this->yMin = $y; - $yMinX = $x; - } - if ($y > $this->yMax) { - $this->yMax = $y; - $yMaxX = $x; - } if (!is_null($previousY)) { $averageAbsSlope += abs($y - $previousY); // just add up all the Y differences $secants[$previousX] = ($y - $previousY) / $deltaX; @@ -36,7 +38,6 @@ namespace NeatCharts { $previousY = $y; $previousX = $x; } - $this->yRange = $this->yMax - $this->yMin; $averageAbsSlope /= $this->yRange * $deltaX; // turn this absolute-deltas total into a slope if ($this->options['smoothed']) { @@ -140,15 +141,13 @@ namespace NeatCharts { } $chartPoints = 'M'; - $chartSplines = 'M'. - $this->transformX($this->xMin).','. - $this->transformY($chartData[$this->xMin]); if ($this->options['smoothed']) { + $chartPoints .= $this->transformX($this->xMin).','.$this->transformY($chartData[$this->xMin]); foreach ($chartData as $x => $y) { $controlX = $deltaX / 3 / sqrt(1 + $tangents[$x]**2); $controlY = $tangents[$x] * $controlX; if ($x != $this->xMin) { - $chartSplines .= ' S'. + $chartPoints .= ' S'. $this->transformX($x - $controlX).','. $this->transformY($y - $controlY).' '. $this->transformX($x).','. @@ -166,31 +165,29 @@ namespace NeatCharts { $chartID = rand(); $this->output = ' - - + + - + - + - '.( $this->options['yAxisEnabled'] || $this->options['xAxisEnabled'] ? ' + '.( $this->options['yAxisEnabled'] || $this->options['xAxisEnabled'] ? ' + shape-rendering="crispEdges"> + font-size="'.( $this->options['fontSize'] ).'px"> '.( $gridText ).' ' : '').' - + '.($this->options['yAxisZero'] ? ' + ' : '').' '; diff --git a/src/NeatCharts/NeatChart.php b/src/NeatCharts/NeatChart.php index bb442b3..0397227 100644 --- a/src/NeatCharts/NeatChart.php +++ b/src/NeatCharts/NeatChart.php @@ -10,7 +10,8 @@ namespace NeatCharts { 'smoothed' => false, 'fontSize' => 15, 'yAxisEnabled'=>true, - 'xAxisEnabled'=>false + 'xAxisEnabled'=>false, + 'yAxisZero'=>false ]; protected $width; @@ -24,11 +25,6 @@ namespace NeatCharts { protected $yRange; protected $padding = ['top'=>10, 'right'=>10, 'bottom'=>10, 'left'=>10]; - protected function arrayGet($array, $key, $default = NULL) - { - return isset($array[$key]) ? $array[$key] : $default; - } - protected function labelFormat($float, $places, $minPlaces = 0) { $value = number_format($float, max($minPlaces, $places)); // add a trailing space if there's no decimal @@ -57,7 +53,29 @@ namespace NeatCharts { return $precision; } - public function __construct($chartData, $options = []) { + protected function setWindow($chartData) { + end($chartData); + $this->xMax = key($chartData); + reset($chartData); + $this->xMin = key($chartData); + $this->xRange = $this->xMax - $this->xMin; + $this->yMin = ($this->options['yAxisZero'] ? 0 : INF); + $this->yMax = -INF; + + foreach ($chartData as $x => $y) { + if ($y < $this->yMin) { + $this->yMin = $y; + $yMinX = $x; + } + if ($y > $this->yMax) { + $this->yMax = $y; + $yMaxX = $x; + } + } + $this->yRange = $this->yMax - $this->yMin; + } + + final public function __construct($chartData, $options = []) { $this->setOptions($options); $this->setData($chartData); }