Skip to content

Commit

Permalink
Updates for SVGGraph 3.3
Browse files Browse the repository at this point in the history
  • Loading branch information
goat1000 committed Nov 22, 2019
1 parent 74a4f39 commit a87f4ce
Show file tree
Hide file tree
Showing 32 changed files with 774 additions and 243 deletions.
223 changes: 223 additions & 0 deletions ArrayGraph.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<?php
/**
* Copyright (C) 2019 Graham Breach
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* For more information, please contact <[email protected]>
*/

namespace Goat1000\SVGGraph;

class ArrayGraph extends Graph {

use MultiGraphTrait {
values as mgValues;
}

protected $inner_options;
protected $inner_types;
protected $raw_data;
protected $raw_settings;

public function __construct($w, $h, array $settings, array $fixed_settings = [])
{
$this->raw_settings = $settings;
parent::__construct($w, $h, $settings, $fixed_settings);
}

/**
* Stores raw values to make it easier to set up subgraphs
*/
public function values($values)
{
$this->raw_data = $values;
return $this->mgValues($values);
}

/**
* Now create the subgraphs
*/
public function checkValues()
{
parent::checkValues();
$opt = [
'keep_colour_order' => true,
'back_stroke_width' => 0,
'back_colour' => 'none',
'title' => null,
];
$opt = array_merge($this->raw_settings, $opt);

$graphs = $this->getLayout();
foreach($graphs as $graph) {
$s = new Subgraph($graph['type'], $graph['x'], $graph['y'],
$graph['w'], $graph['h'], array_merge($opt, $graph['options']));
$s->values($this->raw_data);
$s->setColours($this->colours);
$this->subgraphs[] = $s;
}
}

public function draw()
{
// all drawing is done by subgraphs
return $this->underShapes() . $this->overShapes();
}

/**
* Returns the list of graphs to be drawn and where to draw them
*/
private function getLayout()
{
// find which datasets are going in each graph
$enabled_datasets = $this->multi_graph->getEnabledDatasets();
$graph_datasets = $this->getOption('array_graph_dataset');
if($graph_datasets === null) {

// default is one dataset per graph
$graph_datasets = [];
foreach($enabled_datasets as $d) {
$graph_datasets[] = [$d];
}
} else {

// need to check each of the selected datasets is enabled
$new_outer = [];
foreach($graph_datasets as $gd) {
$new_inner = [];
if(is_array($gd)) {
foreach($gd as $d) {
if(!in_array($d, $enabled_datasets))
continue;
$new_inner[] = $d;
}
} else {
if(!in_array($gd, $enabled_datasets))
continue;
$new_inner[] = $gd;
}

if(count($new_inner) > 0)
$new_outer[] = $new_inner;
}
$graph_datasets = $new_outer;
}

$graph_count = count($graph_datasets);
$cols = $this->getOption('array_graph_columns');
if($cols === null || $cols === 'auto') {
$cols = $this->calcCols($graph_count);
} else {
$cols = (int)$cols;
if($cols < 1)
throw new \Exception('Invalid array_graph_columns value: ' . $cols);
if($this->width / $cols < 20)
throw new \Exception('Option array_graph_columns too large: ' . $cols);
}
$rows = ceil($graph_count / $cols);
$w = $this->width / $cols;
$h = $this->height / $rows;

$inner_options = $this->getOption('array_graph_options');
if($inner_options !== null) {
if(!is_array($inner_options))
throw new \Exception('Option array_graph_options is not an array');

// check if it is an array of arrays
if(!isset($inner_options[0]) || !is_array($inner_options[0])) {

// put single array into outer array
$inner_options = [ $inner_options ];
}
} else {

// no special options
$inner_options = [ [ ] ];
}
$o_count = count($inner_options);

$index = 0;
$layout = [];
$last_row_count = $graph_count % $cols;
$last_row_offset = 0;
$align = $this->getOption('array_graph_align');
if($last_row_count && $align != 'left') {
$mult = $align == 'right' ? 1.0 : 0.5;
$last_row_offset = ($this->width - $last_row_count * $w) * $mult;
}

foreach($graph_datasets as $d) {
$col = $index % $cols;
$row = floor($index / $cols);
$x_offset = ($row == $rows - 1 ? $x_offset = $last_row_offset : 0);

$options = $inner_options[$index % $o_count];
$options['dataset'] = $d;

$g = [
'x' => $x_offset + $w * $col,
'y' => $h * $row,
'w' => $w,
'h' => $h,
'type' => $this->getOption(['array_graph_type', $index], ['@', 'PieGraph']),
'options' => $options,
];

$layout[] = $g;
++$index;
}

return $layout;
}

/**
* Calculates the best number of columns
*/
private function calcCols($count)
{
if($count === 1)
return 1;

$w = $this->width;
$h = $this->height;
if($count === 2)
return $w > $h ? 2 : 1;

$matches = [];
for($c = 1; $c <= $count; ++$c) {
$r = ceil($count / $c);
$bw = $w / $c;
$bh = $h / $r;
$area = $bw * $bh;
$aspect = $bw < $bh ? $bw / $h : $bh / $bw;
$score = $area * ($aspect + 1);

$matches[] = [
'c' => $c,
'r' => $r,
'area' => $area,
'aspect' => $aspect,
'score' => $score,
];
}

usort($matches, function($a, $b) { return $a['score'] - $b['score']; });
$winner = array_pop($matches);
return $winner['c'];
}

}

22 changes: 15 additions & 7 deletions BarAndLineGraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected function draw()

// find the lines
$chunk_count = count($this->multi_graph);
$lines = $this->line_dataset;
$lines = $this->getOption('line_dataset');
$line_breaks = [];
$line_points = [];
$points = [];
Expand All @@ -82,23 +82,26 @@ protected function draw()
$marker_offset = $this->x_axes[$this->main_x_axis]->unit() / 2;

// draw bars, store line points
$datasets = $this->multi_graph->getEnabledDatasets();
$line_dataset = 0;
$bars = '';
foreach($this->multi_graph as $bnum => $itemlist) {
$item = $itemlist[0];
$bar_pos = $this->gridPosition($item, $bnum);
if(!is_null($bar_pos)) {
if($bar_pos !== null) {
for($j = 0; $j < $chunk_count; ++$j) {
if(!in_array($j, $datasets))
continue;
$y_axis = $this->datasetYAxis($j);
$item = $itemlist[$j];

if(array_key_exists($j, $lines)) {
$line_dataset = $j;
if($line_breaks[$line_dataset] && is_null($item->value) &&
if($line_breaks[$line_dataset] && $item->value === null &&
count($points[$line_dataset]) > 0) {
$line_points[$line_dataset][] = $points[$line_dataset];
$points[$line_dataset] = [];
} elseif(!is_null($item->value)) {
} elseif($item->value !== null) {
$x = $bar_pos + $marker_offset;
$y = $this->gridY($item->value, $y_axis);
$points[$line_dataset][] = [$x, $y, $item, $line_dataset, $bnum];
Expand Down Expand Up @@ -148,15 +151,20 @@ protected function draw()
protected function barSetup()
{
parent::barSetup();
$datasets = $chunk_count = count($this->multi_graph);
for($i = 0, $bar = 0; $i < $datasets; ++$i) {
$datasets = $this->multi_graph->getEnabledDatasets();
$chunk_count = count($datasets);
$bar = 0;
foreach($datasets as $i) {
// reduce chunk count for lines, add bars to list
if(array_key_exists($i, $this->line_datasets))
--$chunk_count;
else
$this->bar_datasets[$i] = $bar++;
}

if(count($this->bar_datasets) < 1)
throw new \Exception('No bar datasets enabled');

list($chunk_width, $bspace, $chunk_unit_width) =
$this->barPosition($this->bar_width, $this->bar_width_min,
$this->x_axes[$this->main_x_axis]->unit(), $chunk_count, $this->bar_space,
Expand All @@ -171,7 +179,7 @@ protected function barSetup()
protected function barX($item, $index, &$bar, $axis, $dataset)
{
$bar_x = $this->gridPosition($item, $index);
if(is_null($bar_x))
if($bar_x === null)
return null;

// relative position of bars stored in barSetup()
Expand Down
9 changes: 5 additions & 4 deletions BarGraphTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@ protected function draw()
*/
protected function drawBars()
{
$dataset = $this->getOption(['dataset', 0], 0);
$this->barSetup();
$this->colourSetup($this->values->itemsCount());
$this->colourSetup($this->values->itemsCount($dataset));

$bars = '';
foreach($this->values[0] as $bnum => $item) {
$this->setBarLegendEntry(0, $bnum, $item);
$bars .= $this->drawBar($item, $bnum);
foreach($this->values[$dataset] as $bnum => $item) {
$this->setBarLegendEntry($dataset, $bnum, $item);
$bars .= $this->drawBar($item, $bnum, 0, null, $dataset);
}
return $bars;
}
Expand Down
Loading

0 comments on commit a87f4ce

Please sign in to comment.