PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.23
E2Pdf – Export Pdf Tool for WordPress v1.32.23
1.32.49 1.32.48 1.32.43 1.32.40 1.32.34 1.32.32 1.32.31 1.32.26 1.32.22 1.32.23 1.32.18 1.32.17 1.32.15 trunk 1.00.00 1.00.13 1.01.01 1.02.02 1.03.07 1.04.07 1.05.03 1.06.02 1.07.11 1.08.00 1.08.06 All 72 releases
e2pdf / vendors / svggraph / BarGraphTrait.php

BarGraphTrait.php in E2Pdf – Export Pdf Tool for WordPress 1.32.23, at vendors/svggraph/BarGraphTrait.php

302 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright (C) 2019-2022 Graham Breach
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19 * For more information, please contact <graham@goat1000.com>
20 */
21
22 namespace Goat1000\SVGGraph;
23
24 trait BarGraphTrait {
25
26 // these are filled in by barSetup()
27 protected $calculated_bar_width;
28 protected $calculated_bar_space;
29
30 /**
31 * Draws the graph
32 */
33 protected function draw()
34 {
35 $this->setup();
36
37 $body = $this->grid();
38 $bars = $this->drawBars();
39 $bar_group = $this->barGroup();
40 if(!empty($bar_group))
41 $bars = $this->element('g', $bar_group, null, $bars);
42
43 $body .= $this->underShapes();
44 $body .= $bars;
45 $body .= $this->overShapes();
46 $body .= $this->axes();
47 return $body;
48 }
49
50 /**
51 * Draws the bars
52 */
53 protected function drawBars()
54 {
55 $dataset = $this->getOption(['dataset', 0], 0);
56 $this->barSetup();
57
58 $bars = '';
59 foreach($this->values[$dataset] as $bnum => $item) {
60 $this->setBarLegendEntry($dataset, $bnum, $item);
61 $bars .= $this->drawBar($item, $bnum, 0, null, $dataset);
62 }
63 return $bars;
64 }
65
66 /**
67 * Returns the width of a bar
68 */
69 protected function barWidth()
70 {
71 $bw = $this->getOption('bar_width');
72 if(is_numeric($bw) && $bw >= 1)
73 return $bw;
74 $unit_w = $this->x_axes[$this->main_x_axis]->unit();
75 $bw = $unit_w - $this->getOption('bar_space');
76 return max(1, $bw, $this->getOption('bar_width_min'));
77 }
78
79 /**
80 * Initialize bars
81 */
82 protected function barSetup()
83 {
84 $width = $this->barWidth();
85 $space = $this->barSpace($width);
86 $this->setBarWidth($width, $space);
87 }
88
89 /**
90 * Sets up the width of the bar and the spacing
91 */
92 protected function setBarWidth($width, $space)
93 {
94 $this->calculated_bar_width = $width;
95 $this->calculated_bar_space = $this->getOption('datetime_keys') ? 0 : $space;
96 }
97
98 /**
99 * Returns an array of attributes for whole group of bars
100 */
101 protected function barGroup()
102 {
103 $group = [];
104 if($this->getOption('semantic_classes'))
105 $group['class'] = 'series';
106 $shadow_id = $this->defs->getShadow();
107 if($shadow_id !== null)
108 $group['filter'] = 'url(#' . $shadow_id . ')';
109 return $group;
110 }
111
112 /**
113 * Sets the legend entry for a bar
114 */
115 protected function setBarLegendEntry($dataset, $index, DataItem $item)
116 {
117 $bar = ['fill' => $this->getColour($item, $index, $dataset)];
118 $this->setStroke($bar, $item, $index, $dataset);
119 $this->setLegendEntry($dataset, $index, $item, $bar);
120 }
121
122 /**
123 * Returns the SVG code for a bar
124 */
125 protected function drawBar(DataItem $item, $index, $start = 0, $axis = null,
126 $dataset = 0, $options = [])
127 {
128 if($item->value === null)
129 return '';
130
131 $bar = $this->barDimensions($item, $index, $start, $axis, $dataset);
132 if(empty($bar))
133 return '';
134
135 // if the bar is empty and no legend or labels to show give up now
136 if((string)$bar['height'] == '0' && !$this->getOption('legend_show_empty') &&
137 !$this->getOption('show_data_labels'))
138 return '';
139
140 $this->setStroke($bar, $item, $index, $dataset);
141 $bar['fill'] = $this->getColour($item, $index, $dataset);
142
143 if($this->getOption('semantic_classes'))
144 $bar['class'] = 'series' . $dataset;
145
146 $label_shown = $this->addDataLabel($dataset, $index, $bar, $item,
147 $bar['x'], $bar['y'], $bar['width'], $bar['height']);
148
149 if($this->getOption('show_tooltips'))
150 $this->setTooltip($bar, $item, $dataset, $item->key, $item->value,
151 $label_shown);
152 if($this->getOption('show_context_menu'))
153 $this->setContextMenu($bar, $dataset, $item, $label_shown);
154
155 $round = max($this->getItemOption('bar_round', $dataset, $item), 0);
156 if($round > 0) {
157 // don't allow the round corner to be more than 1/2 bar width or height
158 $bar['rx'] = $bar['ry'] = min($round, $bar['width'] / 2, $bar['height'] / 2);
159 }
160
161 $bar_part = $this->element('rect', $bar);
162 return $this->getLink($item, $item->key, $bar_part);
163 }
164
165 /**
166 * Returns the space before a bar
167 */
168 protected function barSpace($bar_width)
169 {
170 return max(0, ($this->x_axes[$this->main_x_axis]->unit() - $bar_width) / 2);
171 }
172
173 /**
174 * Returns an array with x, y, width and height set
175 */
176 protected function barDimensions($item, $index, $start, $axis, $dataset)
177 {
178 $bar = [];
179 $bar_x = $this->barX($item, $index, $bar, $axis, $dataset);
180 if($bar_x === null)
181 return [];
182
183 $y_pos = $this->barY($item->value, $bar, $start, $axis);
184 if($y_pos === null)
185 return [];
186 return $bar;
187 }
188
189 /**
190 * Fills in the x and width of bar
191 */
192 protected function barX($item, $index, &$bar, $axis, $dataset)
193 {
194 $bar_x = $this->gridPosition($item, $index);
195 if($bar_x === null)
196 return null;
197
198 $bar['x'] = $bar_x + $this->calculated_bar_space;
199 $bar['width'] = $this->calculated_bar_width;
200 return $bar_x;
201 }
202
203 /**
204 * Fills in the y and height of a bar
205 * @return number unclamped bar position
206 */
207 protected function barY($value, &$bar, $start = null, $axis = null)
208 {
209 if($start)
210 $value += $start;
211
212 $startpos = $start === null ? $this->originY($axis) :
213 $this->gridY($start, $axis);
214 if($startpos === null)
215 $startpos = $this->originY($axis);
216 $pos = $this->gridY($value, $axis);
217 if($pos === null) {
218 $bar['height'] = 0;
219 } else {
220 $l1 = $this->clampVertical($startpos);
221 $l2 = $this->clampVertical($pos);
222 $bar['y'] = min($l1, $l2);
223 $bar['height'] = abs($l1-$l2);
224 }
225 return $pos;
226 }
227
228 /**
229 * Override to check minimum space requirement
230 */
231 protected function addDataLabel($dataset, $index, &$element, &$item,
232 $x, $y, $w, $h, $content = null, $duplicate = true)
233 {
234 if($h < $this->getOption(['data_label_min_space', $dataset]))
235 return false;
236 return parent::addDataLabel($dataset, $index, $element, $item, $x, $y,
237 $w, $h, $content, $duplicate);
238 }
239
240 /**
241 * Returns the position for a data label
242 */
243 public function dataLabelPosition($dataset, $index, &$item, $x, $y, $w, $h,
244 $label_w, $label_h)
245 {
246 list($pos,$end) = parent::dataLabelPosition($dataset, $index, $item,
247 $x, $y, $w, $h, $label_w, $label_h);
248 $bpos = $this->getOption('bar_label_position');
249 if(!empty($bpos))
250 $pos = $bpos;
251
252 if($label_h > $h && Graph::isPositionInside($pos))
253 $pos = str_replace(['top','middle','bottom'], 'outside top inside ', $pos);
254
255 // flip top/bottom for negative values
256 if($item !== null && $item->value < 0) {
257 if(strpos($pos, 'top') !== false)
258 $pos = str_replace('top','bottom', $pos);
259 elseif(strpos($pos, 'above') !== false)
260 $pos = str_replace('above','below', $pos);
261 elseif(strpos($pos, 'below') !== false)
262 $pos = str_replace('below','above', $pos);
263 elseif(strpos($pos, 'bottom') !== false)
264 $pos = str_replace('bottom','top', $pos);
265 }
266
267 return [$pos, $end];
268 }
269
270 /**
271 * Returns the style options for bar labels
272 */
273 public function dataLabelStyle($dataset, $index, &$item)
274 {
275 $style = parent::dataLabelStyle($dataset, $index, $item);
276
277 // bar label settings can override global settings
278 $opts = [
279 'font' => 'bar_label_font',
280 'font_size' => 'bar_label_font_size',
281 'font_weight' => 'bar_label_font_weight',
282 'colour' => 'bar_label_colour',
283 'altcolour' => 'bar_label_colour_above',
284 'space' => 'bar_label_space',
285 ];
286 foreach($opts as $key => $opt)
287 if(isset($this->settings[$opt]))
288 $style[$key] = $this->settings[$opt];
289 return $style;
290 }
291
292 /**
293 * Return box for legend
294 */
295 public function drawLegendEntry($x, $y, $w, $h, $entry)
296 {
297 $bar = ['x' => $x, 'y' => $y, 'width' => $w, 'height' => $h];
298 return $this->element('rect', $bar, $entry->style);
299 }
300 }
301
302