mirror of
https://github.com/seigler/neat-charts
synced 2025-07-27 01:16:09 +00:00
minor refactor to make bar charts easier
This commit is contained in:
parent
bbe218d7fe
commit
cfa5fafa21
2 changed files with 81 additions and 76 deletions
|
@ -78,67 +78,11 @@ namespace NeatCharts {
|
|||
http://vis4.net/blog/posts/doing-the-line-charts-right/
|
||||
*/
|
||||
$aspectRatio = max(0.25, min(0.75, 1 / $averageAbsSlope));
|
||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||
if (isset($this->options['height'])) {
|
||||
$this->height = $this->options['height'] - $this->padding['top'] - $this->padding['bottom'];
|
||||
} else {
|
||||
$this->height = floor($aspectRatio * $this->width);
|
||||
$this->options['height'] = $this->height + $this->padding['top'] + $this->padding['bottom'];
|
||||
if (!isset($this->options['height'])) {
|
||||
$this->options['height'] = floor($aspectRatio * $this->options['width']);
|
||||
}
|
||||
$this->padding['left'] = $this->padding['right'] = $this->options['fontSize'] / 2;
|
||||
if ($this->options['yAxisEnabled']) {
|
||||
$numLabels = 2 + ceil($this->height / $this->options['fontSize'] / 6);
|
||||
$labelInterval = $this->yRange / $numLabels;
|
||||
$labelModulation = 10 ** (1 + floor(-log($this->yRange / $numLabels, 10)));
|
||||
// 0.1 here is a fudge factor so we get multiples of 2.5 a little more often
|
||||
if (fmod($labelInterval * $labelModulation, 2.5) < fmod($labelInterval * $labelModulation, 2) + 0.1) {
|
||||
$labelModulation /= 2.5;
|
||||
} else {
|
||||
$labelModulation /= 2;
|
||||
}
|
||||
$labelInterval = ceil($labelInterval * $labelModulation) / $labelModulation;
|
||||
$labelPrecision = $this->getPrecision($labelInterval);
|
||||
|
||||
$this->padding['left'] = $this->options['fontSize'] * 0.6 * (
|
||||
1 + max(1, ceil(log($this->yMax, 10))) + $this->getPrecision($labelInterval)
|
||||
) + 10;
|
||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||
|
||||
// Top and bottom grid lines
|
||||
$gridLines =
|
||||
'M10,0 '.$this->width.',0 '.
|
||||
' M10,'.$this->height.','.$this->width.','.$this->height;
|
||||
|
||||
// Top and bottom grid labels
|
||||
$gridText =
|
||||
'<text text-anchor="end" x="'.(0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4).'">'.($this->labelFormat($this->yMax, $labelPrecision + 1)).'</text>' .
|
||||
'<text text-anchor="end" x="'.(0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4 + $this->height).'">'.($this->labelFormat($this->yMin, $labelPrecision + 1)).'</text>';
|
||||
|
||||
// Main labels and grid lines
|
||||
for (
|
||||
$labelY = $this->yMin - fmod($this->yMin, $labelInterval) + $labelInterval; // Start at the first "nice" Y value > min
|
||||
$labelY < $this->yMax; // Keep going until max
|
||||
$labelY += $labelInterval // Add Interval each iteration
|
||||
) {
|
||||
$labelHeight = $this->transformY($labelY);
|
||||
if ( // label is not too close to the min or max
|
||||
$labelHeight < $this->height - 1.5 * $this->options['fontSize'] &&
|
||||
$labelHeight > $this->options['fontSize'] * 1.5
|
||||
) {
|
||||
$gridText .= '<text text-anchor="end" x="-'.(0.25 * $this->options['fontSize']).'" y="'.($labelHeight + $this->options['fontSize'] * 0.4).'">'.$this->labelFormat($labelY, $labelPrecision).'</text>';
|
||||
$gridLines .= ' M0,'.$labelHeight.' '.$this->width.','.$labelHeight;
|
||||
} else if ( // label is too close
|
||||
$labelHeight < $this->height - $this->options['fontSize'] * 0.75 &&
|
||||
$labelHeight > $this->options['fontSize'] * 0.75
|
||||
) {
|
||||
$gridLines .= ' M'.( // move grid line over when it's very close to the min or max label
|
||||
$labelHeight < $this->height - $this->options['fontSize'] / 2 && $labelHeight > $this->options['fontSize'] / 2 ? 0 : $this->options['fontSize'] / 2
|
||||
).','.$labelHeight.' '.$this->width.','.$labelHeight;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||
}
|
||||
$gridLabelsXML = parent::buildGridLabelXML();
|
||||
|
||||
$chartPoints = 'M';
|
||||
if ($this->options['smoothed']) {
|
||||
|
@ -163,6 +107,7 @@ namespace NeatCharts {
|
|||
}
|
||||
|
||||
$chartID = rand();
|
||||
|
||||
$this->output = '<svg viewBox="-'.( $this->padding['left'] ).' -'.( $this->padding['top'] ).' '.( $this->options['width'] ).' '.( $this->options['height'] ).'" width="'.( $this->options['width'] ).'" height="'.( $this->options['height'] ).'" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<marker id="neatchart-markerCircle-'.( $chartID ).'" markerWidth="2" markerHeight="2" refX="1" refY="1" markerUnits="strokeWidth">
|
||||
|
@ -174,22 +119,7 @@ namespace NeatCharts {
|
|||
<stop offset="100%" stop-color="'.( $this->options['lineColor'] ).'" stop-opacity="1"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g class="neatchart">'.( $this->options['yAxisEnabled'] || $this->options['xAxisEnabled'] ? '
|
||||
<g class="chart__gridLines"
|
||||
fill="none"
|
||||
stroke="'.( $this->options['labelColor'] ).'"
|
||||
stroke-width="1"
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-dasharray="2, 2"
|
||||
shape-rendering="crispEdges">
|
||||
<path class="chart__gridLinePaths" d="'.( $gridLines ).'" />
|
||||
</g>
|
||||
<g class="chart__gridLabels"
|
||||
fill="'.( $this->options['labelColor'] ).'"
|
||||
font-family="monospace"
|
||||
font-size="'.( $this->options['fontSize'] ).'px">
|
||||
'.( $gridText ).'
|
||||
</g>' : '').'
|
||||
<g class="neatchart">'.( $this->options['yAxisEnabled'] || $this->options['xAxisEnabled'] ? $gridLabelsXML : '').'
|
||||
<g class="chart__plotLine"
|
||||
fill="none"
|
||||
stroke-width="'.( $this->options['fontSize'] / 3 ).'"
|
||||
|
|
|
@ -75,6 +75,81 @@ namespace NeatCharts {
|
|||
$this->yRange = $this->yMax - $this->yMin;
|
||||
}
|
||||
|
||||
protected function buildGridLabelXML() {
|
||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||
$this->height = $this->options['height'] - $this->padding['top'] - $this->padding['bottom'];
|
||||
if ($this->options['yAxisEnabled'] || $this->options['xAxisEnabled']) {
|
||||
$numLabels = 2 + ceil($this->height / $this->options['fontSize'] / 6);
|
||||
$labelInterval = $this->yRange / $numLabels;
|
||||
$labelModulation = 10 ** (1 + floor(-log($this->yRange / $numLabels, 10)));
|
||||
// 0.1 here is a fudge factor so we get multiples of 2.5 a little more often
|
||||
if (fmod($labelInterval * $labelModulation, 2.5) < fmod($labelInterval * $labelModulation, 2) + 0.1) {
|
||||
$labelModulation /= 2.5;
|
||||
} else {
|
||||
$labelModulation /= 2;
|
||||
}
|
||||
$labelInterval = ceil($labelInterval * $labelModulation) / $labelModulation;
|
||||
$labelPrecision = $this->getPrecision($labelInterval);
|
||||
|
||||
$this->padding['left'] = $this->options['fontSize'] * 0.6 * (
|
||||
1 + max(1, ceil(log($this->yMax, 10))) + $this->getPrecision($labelInterval)
|
||||
) + 10;
|
||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||
|
||||
// Top and bottom grid lines
|
||||
$gridLines =
|
||||
'M10,0 '.$this->width.',0 '.
|
||||
' M10,'.$this->height.','.$this->width.','.$this->height;
|
||||
|
||||
// Top and bottom grid labels
|
||||
$gridText =
|
||||
'<text text-anchor="end" x="'.(0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4).'">'.($this->labelFormat($this->yMax, $labelPrecision + 1)).'</text>' .
|
||||
'<text text-anchor="end" x="'.(0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4 + $this->height).'">'.($this->labelFormat($this->yMin, $labelPrecision + 1)).'</text>';
|
||||
|
||||
// Main labels and grid lines
|
||||
for (
|
||||
$labelY = $this->yMin - fmod($this->yMin, $labelInterval) + $labelInterval; // Start at the first "nice" Y value > min
|
||||
$labelY < $this->yMax; // Keep going until max
|
||||
$labelY += $labelInterval // Add Interval each iteration
|
||||
) {
|
||||
$labelHeight = $this->transformY($labelY);
|
||||
if ( // label is not too close to the min or max
|
||||
$labelHeight < $this->height - 1.5 * $this->options['fontSize'] &&
|
||||
$labelHeight > $this->options['fontSize'] * 1.5
|
||||
) {
|
||||
$gridText .= '<text text-anchor="end" x="-'.(0.25 * $this->options['fontSize']).'" y="'.($labelHeight + $this->options['fontSize'] * 0.4).'">'.$this->labelFormat($labelY, $labelPrecision).'</text>';
|
||||
$gridLines .= ' M0,'.$labelHeight.' '.$this->width.','.$labelHeight;
|
||||
} else if ( // label is too close
|
||||
$labelHeight < $this->height - $this->options['fontSize'] * 0.75 &&
|
||||
$labelHeight > $this->options['fontSize'] * 0.75
|
||||
) {
|
||||
$gridLines .= ' M'.( // move grid line over when it's very close to the min or max label
|
||||
$labelHeight < $this->height - $this->options['fontSize'] / 2 && $labelHeight > $this->options['fontSize'] / 2 ? 0 : $this->options['fontSize'] / 2
|
||||
).','.$labelHeight.' '.$this->width.','.$labelHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return '
|
||||
<g class="chart__gridLines"
|
||||
fill="none"
|
||||
stroke="'.( $this->options['labelColor'] ).'"
|
||||
stroke-width="1"
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-dasharray="2, 2"
|
||||
shape-rendering="crispEdges">
|
||||
<path class="chart__gridLinePaths" d="'.( $gridLines ).'" />
|
||||
</g>
|
||||
<g class="chart__gridLabels"
|
||||
fill="'.( $this->options['labelColor'] ).'"
|
||||
font-family="monospace"
|
||||
font-size="'.( $this->options['fontSize'] ).'px">
|
||||
'.( $gridText ).'
|
||||
</g>';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
final public function __construct($chartData, $options = []) {
|
||||
$this->setOptions($options);
|
||||
$this->setData($chartData);
|
||||
|
@ -82,7 +157,7 @@ namespace NeatCharts {
|
|||
|
||||
public function setOptions($options) {
|
||||
$this->options = array_replace($this->options, $options);
|
||||
$this->padding['left'] = $this->options['fontSize'] * 5;
|
||||
$this->padding['left'] = $this->padding['right'] = $this->options['fontSize'] / 2;
|
||||
$this->padding['top'] = $this->padding['bottom'] = $this->options['fontSize'];
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue