PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.49
E2Pdf – Export Pdf Tool for WordPress v1.32.49
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 / PointGraph.php

PointGraph.php in E2Pdf – Export Pdf Tool for WordPress 1.32.49, at vendors/svggraph/PointGraph.php

363 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright (C) 2010-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 /**
25 * Abstract base class for graphs which use markers
26 */
27 abstract class PointGraph extends GridGraph {
28
29 protected $markers = [];
30 protected $marker_ids = [];
31 protected $marker_link_ids = [];
32 protected $marker_types = [];
33
34 private $x_offset = null;
35
36 public function __construct($width, $height, array $settings, array $fixed_settings = [])
37 {
38 $fs = [];
39 if(isset($settings['block_position_markers']) && $settings['block_position_markers'])
40 $fs['label_centre'] = true;
41 $fs = array_merge($fs, $fixed_settings);
42 parent::__construct($width, $height, $settings, $fs);
43 }
44
45 /**
46 * Adds a marker to the list
47 */
48 public function addMarker($x, $y, $item, $extra = null, $set = 0,
49 $legend = true)
50 {
51 $m = new Marker($x, $y, $item, $extra);
52 if($this->specialMarker($set, $item))
53 $m->id = $this->createSingleMarker($set, $item);
54
55 $this->markers[$set][] = $m;
56 $index = count($this->markers[$set]) - 1;
57
58 if($legend) {
59 $legend_info = ['dataset' => $set, 'index' => $index];
60 $this->setLegendEntry($set, $index, $item, $legend_info);
61 }
62 }
63
64 /**
65 * Draws (linked) markers on the graph
66 */
67 public function drawMarkers()
68 {
69 if($this->getOption('marker_size') == 0 || count($this->markers) == 0)
70 return '';
71
72 $this->createMarkers();
73
74 $markers = '';
75 foreach($this->markers as $set => $data) {
76 if($this->marker_ids[$set] && count($data))
77 $markers .= $this->drawMarkerSet($set, $data);
78 }
79
80 $group = [];
81 if($this->getOption('semantic_classes'))
82 $group['class'] = 'series';
83 $shadow_id = $this->defs->getShadow();
84 if($shadow_id !== null)
85 $group['filter'] = 'url(#' . $shadow_id . ')';
86 if(!empty($group))
87 $markers = $this->element('g', $group, null, $markers);
88 return $markers;
89 }
90
91 /**
92 * Draws a single set of markers
93 */
94 protected function drawMarkerSet($set, &$marker_data)
95 {
96 $markers = '';
97 foreach($marker_data as $m)
98 $markers .= $this->getMarker($m, $set);
99 return $markers;
100 }
101
102
103 /**
104 * Returns a marker element
105 */
106 protected function getMarker($marker, $set)
107 {
108 $id = isset($marker->id) ? $marker->id : $this->marker_ids[$set];
109 $use = ['x' => $marker->x, 'y' => $marker->y];
110
111 if(is_array($marker->extra))
112 $use = array_merge($marker->extra, $use);
113 if($this->getOption('semantic_classes'))
114 $use['class'] = 'series' . $set;
115 if($this->getOption('show_tooltips'))
116 $this->setTooltip($use, $marker->item, $set, $marker->key, $marker->value);
117 if($this->getOption('show_context_menu'))
118 $this->setContextMenu($use, $set, $marker->item);
119
120 if($this->getLinkURL($marker->item, $marker->key)) {
121 $id = $this->marker_link_ids[$id];
122 $element = $this->getLink($marker->item, $marker->key,
123 $this->defs->useSymbol($id, $use));
124 } else {
125 $element = $this->defs->useSymbol($id, $use);
126 }
127
128 return $element;
129 }
130
131 /**
132 * Return a centred marker for the given set
133 */
134 public function drawLegendEntry($x, $y, $w, $h, $entry)
135 {
136 if(!isset($entry->style['dataset']))
137 return '';
138
139 $dataset = $entry->style['dataset'];
140 $index = $entry->style['index'];
141 $marker = $this->markers[$dataset][$index];
142 if(isset($marker->id))
143 $id = $marker->id;
144 elseif(isset($this->marker_ids[$dataset]))
145 $id = $this->marker_ids[$dataset];
146 else
147 return ''; // no marker!
148
149 // if the standard marker is unused, must be a link marker
150 if(!$this->defs->symbolUseCount($id))
151 $id = $this->marker_link_ids[$id];
152
153 // use data stored with legend to look up marker
154 $m = ['x' => $x + $w/2, 'y' => $y + $h/2];
155 return $this->defs->useSymbol($id, $m);
156 }
157
158 /**
159 * Creates a single marker element and its link version
160 */
161 protected function createMarker($type, $size, $fill, $stroke_width,
162 $stroke_colour, $opacity, $angle)
163 {
164 $m_key = md5(implode('|', func_get_args()));
165 if(isset($this->marker_types[$m_key]))
166 return $this->marker_types[$m_key];
167
168 $markers = new Markers($this);
169 $extra = ['cursor' => 'crosshair'];
170 $id = $markers->create($type, $size, $fill, $stroke_width,
171 $stroke_colour, $opacity, $angle, $extra);
172
173 // add link version
174 $link_id = $markers->create($type, $size, $fill, $stroke_width,
175 $stroke_colour, $opacity, $angle);
176 $this->marker_link_ids[$id] = $link_id;
177
178 // save this marker style for reuse
179 $this->marker_types[$m_key] = $id;
180 return $id;
181 }
182
183 /**
184 * Returns true if a marker is different to others in its set
185 */
186 protected function specialMarker($set, &$item)
187 {
188 $null_item = null;
189 if($this->getItemOption('marker_colour', $set, $item, 'colour') !=
190 $this->getItemOption('marker_colour', $set, $null_item))
191 return true;
192
193 $vlist = ['marker_type', 'marker_size', 'marker_stroke_width',
194 'marker_stroke_colour', 'marker_angle', 'marker_opacity'];
195 foreach($vlist as $value)
196 if($this->getItemOption($value, $set, $item) !=
197 $this->getItemOption($value, $set, $null_item))
198 return true;
199 return false;
200 }
201
202 /**
203 * Creates a single marker for the data set
204 */
205 protected function createSingleMarker($set, &$item = null)
206 {
207 $type = $this->getItemOption('marker_type', $set, $item);
208 $size = $this->getItemOption('marker_size', $set, $item);
209 $angle = $this->getItemOption('marker_angle', $set, $item);
210 $opacity = $this->getItemOption('marker_opacity', $set, $item);
211
212 // support gradients/patterns?
213 $gpat = !($this->getOption('marker_solid', true));
214 $mcolour = $this->getItemOption('marker_colour', $set, $item, 'colour');
215 if(empty($mcolour)) {
216 $fill = $this->getColour(null, 0, $set, $gpat, $gpat);
217 } else {
218 // support fill and fillColour
219 $cg = new ColourGroup($this, $item, 0, $set, 'marker_colour', null, 'colour');
220 $fill = $cg->stroke();
221
222 // impose marker_solid option
223 if(!$gpat)
224 $fill = new Colour($this, $fill, false, false);
225 }
226
227 // stroke colour is related to the marker fill colour unless it is 'none'
228 $cg = new ColourGroup($this, $item, 0, $set, 'marker_stroke_colour',
229 $fill->isNone() ? null : $fill);
230 $stroke_colour = $cg->stroke();
231
232 $stroke_width = $stroke_colour === null || $stroke_colour->isNone() ? '' :
233 $this->getItemOption('marker_stroke_width', $set, $item);
234
235 return $this->createMarker($type, $size, $fill, $stroke_width,
236 $stroke_colour, $opacity, $angle);
237 }
238
239 /**
240 * Creates the marker types
241 */
242 protected function createMarkers()
243 {
244 foreach(array_keys($this->markers) as $set) {
245 // set the ID for this data set to use
246 $this->marker_ids[$set] = $this->createSingleMarker($set);
247 }
248 }
249
250 /**
251 * Returns the position for a data label
252 */
253 public function dataLabelPosition($dataset, $index, &$item, $x, $y, $w, $h,
254 $label_w, $label_h)
255 {
256 list($pos, $target) = parent::dataLabelPosition($dataset, $index, $item,
257 $x, $y, $w, $h, $label_w, $label_h);
258
259 // labels don't fit inside markers
260 $pos = str_replace(['inner','inside'], '', $pos);
261 if(strpos($pos, 'middle') !== false && strpos($pos, 'right') === false &&
262 strpos($pos, 'left') === false)
263 $pos = str_replace('middle', 'top', $pos);
264 if(strpos($pos, 'centre') !== false && strpos($pos, 'top') === false &&
265 strpos($pos, 'bottom') === false)
266 $pos = str_replace('centre', 'top', $pos);
267 $pos = 'outside ' . $pos;
268 return [$pos, $target];
269 }
270
271 /**
272 * Add a marker label
273 */
274 public function markerLabel($dataset, $index, &$item, $x, $y)
275 {
276 if(!$this->getOption(['show_data_labels', $dataset]))
277 return false;
278 $s = $this->getItemOption('marker_size', 0, $item);
279 $s2 = $s / 2;
280 $dummy = [];
281 $label = $this->addDataLabel($dataset, $index, $dummy, $item,
282 $x - $s2, $y - $s2, $s, $s, null);
283
284 if(isset($dummy['id']))
285 return $dummy['id'];
286
287 return null;
288 }
289
290 /**
291 * Returns a pair of best fit lines, above and below
292 */
293 public function bestFitLines()
294 {
295 $bbox = new BoundingBox(0, 0, $this->g_width, $this->g_height);
296 $bbox->offset($this->pad_left, $this->pad_top);
297 $bf = new BestFit($this, $bbox);
298
299 foreach($this->markers as $dataset => $mset) {
300 $points = [];
301
302 // create points positioned relative to bottom-left of grid
303 foreach($this->markers[$dataset] as $k => $v)
304 $points[] = new Point($v->x - $bbox->x1, $bbox->y2 - $v->y);
305
306 $bf->add($dataset, $points);
307 }
308 return [ $bf->getAbove(), $bf->getBelow() ];
309 }
310
311 /**
312 * Override to show key and value
313 */
314 protected function formatTooltip(&$item, $dataset, $key, $value)
315 {
316 if($this->getOption('datetime_keys')) {
317 $number_key = new Number($key);
318 $dt = new \DateTime('@' . $number_key);
319 $axis = $this->x_axes[$this->main_x_axis];
320 $text = $axis->format($dt, $this->getOption('tooltip_datetime_format'));
321 } elseif(is_numeric($key)) {
322 $num = new Number($key, $this->getOption('units_tooltip_key'),
323 $this->getOption('units_before_tooltip_key'));
324 $text = $num->format();
325 } else {
326 $text = $key;
327 }
328
329 $num = new Number($value, $this->getOption('units_tooltip'),
330 $this->getOption('units_before_tooltip'));
331 $text .= ', ' . $num->format();
332 return $text;
333 }
334
335 /**
336 * Returns TRUE if the item is visible on the graph
337 */
338 public function isVisible($item, $dataset = 0)
339 {
340 // non-null values should be visible
341 return ($item->value !== null);
342 }
343
344 /**
345 * Override to handle offset caused by block position option
346 */
347 protected function gridPosition($item, $index)
348 {
349 $gp = parent::gridPosition($item, $index);
350 if($gp === null)
351 return null;
352
353 if($this->x_offset === null) {
354 $this->x_offset = 0;
355 if($this->getOption('block_position_markers'))
356 $this->x_offset = parent::gridPosition(null, 1)
357 - parent::gridPosition(null, 0.5);
358 }
359 return $this->x_offset + $gp;
360 }
361 }
362
363