PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.32
E2Pdf – Export Pdf Tool for WordPress v1.32.32
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 / ThreeDTrait.php

ThreeDTrait.php in E2Pdf – Export Pdf Tool for WordPress 1.32.32, at vendors/svggraph/ThreeDTrait.php

368 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright (C) 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 ThreeDTrait {
25
26 // Number of data ranges
27 protected $depth = 1;
28 protected $depth_unit = 1;
29 protected $project_angle;
30
31 /**
32 * Converts x,y,z coordinates into flat x,y
33 */
34 protected function project($x, $y, $z)
35 {
36 $a = deg2rad($this->project_angle);
37 $xp = $z * cos($a);
38 $yp = $z * sin($a);
39 return [$x + $xp, $y - $yp];
40 }
41
42 /**
43 * Adjust axes for block spacing, setting the depth unit
44 */
45 protected function adjustAxes(&$x_len, &$y_len)
46 {
47 // make sure project_angle is in range
48 $this->project_angle = min(89, max(1, $this->getOption('project_angle', 30)));
49
50 $ends = $this->getAxisEnds();
51 $bars = $ends['k_max'][0] - $ends['k_min'][0] + 1;
52 $a = deg2rad($this->project_angle);
53
54 $depth = $this->depth;
55 $u = $x_len / ($bars + $depth * cos($a));
56 $d = $u * $depth * sin($a);
57 if($d > $y_len) {
58 // doesn't fit - use 1/2 y length
59 $d = $y_len / 2;
60 $u = $d / $depth * sin($a);
61 }
62 $c = $u * $depth * cos($a);
63 $x_len -= $c;
64 $y_len -= $d;
65 $this->depth_unit = $u;
66 return [$c, $d];
67 }
68
69 /**
70 * Draws the grid behind the bar / line graph
71 */
72 protected function grid()
73 {
74 $this->calcAxes();
75 $this->calcGrid();
76 if(!$this->getOption('show_grid') ||
77 (!$this->getOption('show_grid_h') && !$this->getOption('show_grid_v')))
78 return '';
79
80 // move to depth
81 $z = $this->depth * $this->depth_unit;
82 list($xd,$yd) = $this->project(0, 0, $z);
83 $x_w = new Number($this->g_width);
84
85 // convert to Number now - more efficient
86 $minus_x_w = new Number(-$this->g_width);
87 $y_h = new Number($this->g_height);
88 $minus_y_h = new Number(-$this->g_height);
89 $xleft = new Number($this->pad_left);
90 $ybottom = new Number($this->height - $this->pad_bottom);
91 $minus_xd = new Number(-$xd);
92 $minus_yd = new Number(-$yd);
93 $xd = new Number($xd);
94 $yd = new Number($yd);
95
96 $back = $subpath = $path = '';
97 $back_colour = new Colour($this, $this->getOption('grid_back_colour'));
98 if(!$back_colour->isNone()) {
99 $dpath = new PathData('M', $xleft, $ybottom, 'v', $minus_y_h, 'l', $xd, $yd);
100 $dpath->add('h', $x_w, 'v', $y_h, 'l', $minus_xd, $minus_yd, 'z');
101 $bpath = [
102 'd' => $dpath,
103 'fill' => $back_colour
104 ];
105 if($this->getOption('grid_back_opacity') != 1)
106 $bpath['fill-opacity'] = $this->getOption('grid_back_opacity');
107 $back = $this->element('path', $bpath);
108 }
109 $back .= $this->getGridStripes();
110 if($this->getOption('show_grid_subdivisions')) {
111 $subpath_h = new PathData;
112 $subpath_v = new PathData;
113 if($this->getOption('show_grid_h')) {
114 $subdivs = $this->getSubDivsY($this->main_y_axis);
115 foreach($subdivs as $y)
116 $subpath_v->add('M', $xleft, $y->position, 'l', $xd, $yd,
117 'l', $x_w, 0);
118 }
119 if($this->getOption('show_grid_v')) {
120 $subdivs = $this->getSubDivsX(0);
121 foreach($subdivs as $x)
122 $subpath_h->add('M', $x->position, $ybottom, 'l', $xd, $yd,
123 'l', 0, $minus_y_h);
124 }
125 if(!($subpath_h->isEmpty() && $subpath_v->isEmpty())) {
126 $colour_h = $this->getOption('grid_subdivision_colour_h',
127 'grid_subdivision_colour', 'grid_colour_h', 'grid_colour');
128 $colour_v = $this->getOption('grid_subdivision_colour_v',
129 'grid_subdivision_colour', 'grid_colour_v', 'grid_colour');
130 $dash_h = $this->getOption('grid_subdivision_dash_h',
131 'grid_subdivision_dash', 'grid_dash_h', 'grid_dash');
132 $dash_v = $this->getOption('grid_subdivision_dash_v',
133 'grid_subdivision_dash', 'grid_dash_v', 'grid_dash');
134 $width_h = $this->getOption('grid_subdivision_stroke_width_h',
135 'grid_subdivision_stroke_width', 'grid_stroke_width_h',
136 'grid_stroke_width');
137 $width_v = $this->getOption('grid_subdivision_stroke_width_v',
138 'grid_subdivision_stroke_width', 'grid_stroke_width_v',
139 'grid_stroke_width');
140
141 if($dash_h == $dash_v && $colour_h == $colour_v && $width_h == $width_v) {
142 $subpath_h->add($subpath_v);
143 $subpath = $this->gridLines($subpath_h, $colour_h, $dash_h, $width_h,
144 ['fill' => 'none']);
145 } else {
146 $subpath = $this->gridLines($subpath_h, $colour_h, $dash_h,$width_h,
147 ['fill' => 'none']) .
148 $this->gridLines($subpath_v, $colour_v, $dash_v, $width_v,
149 ['fill' => 'none']);
150 }
151 }
152 }
153
154 // start with axis lines
155 $path = new PathData('M', $xleft, $ybottom, 'l', $x_w, 0);
156 $path->add('M', $xleft, $ybottom, 'l', 0, $minus_y_h);
157 $path_v = new PathData;
158 $path_h = new PathData;
159 if($this->getOption('show_grid_h')) {
160 $points = $this->getGridPointsY($this->main_y_axis);
161 foreach($points as $y)
162 $path_v->add('M', $xleft, $y->position, 'l', $xd, $yd, 'l', $x_w, 0);
163 }
164 if($this->getOption('show_grid_v')) {
165 $points = $this->getGridPointsX(0);
166 foreach($points as $x)
167 $path_h->add('M', $x->position, $ybottom, 'l', $xd, $yd, 'l', 0, $minus_y_h);
168 }
169
170 $colour_h = $this->getOption('grid_colour_h', 'grid_colour');
171 $colour_v = $this->getOption('grid_colour_v', 'grid_colour');
172 $dash_h = $this->getOption('grid_dash_h', 'grid_dash');
173 $dash_v = $this->getOption('grid_dash_v', 'grid_dash');
174 $width_h = $this->getOption('grid_stroke_width_h', 'grid_stroke_width');
175 $width_v = $this->getOption('grid_stroke_width_v', 'grid_stroke_width');
176
177 if($dash_h == $dash_v && $colour_h == $colour_v && $width_h == $width_v) {
178 $path_h->add($path_v);
179 $path = $this->gridLines($path_h, $colour_h, $dash_h, $width_h,
180 ['fill' => 'none']);
181 } else {
182 $path = $this->gridLines($path_h, $colour_h, $dash_h, $width_h,
183 ['fill' => 'none']) .
184 $this->gridLines($path_v, $colour_v, $dash_v, $width_v,
185 ['fill' => 'none']);
186 }
187
188 return $back . $subpath . $path;
189 }
190
191 /**
192 * Returns the grid stripes as a string
193 */
194 protected function getGridStripes()
195 {
196 if(!$this->getOption('grid_back_stripe'))
197 return '';
198
199 // move to depth
200 $z = $this->depth * $this->depth_unit;
201 list($xd,$yd) = $this->project(0, 0, $z);
202 $x_w = new Number($this->g_width);
203
204 $minus_x_w = new Number(-$this->g_width);
205 $xleft = new Number($this->pad_left);
206 $minus_xd = new Number(-$xd);
207 $minus_yd = new Number(-$yd);
208 $xd = new Number($xd);
209 $yd = new Number($yd);
210
211 // use array of colours if available, otherwise stripe a single colour
212 $colours = is_array($this->getOption('grid_back_stripe_colour')) ?
213 $this->getOption('grid_back_stripe_colour') :
214 [null, $this->getOption('grid_back_stripe_colour')];
215 $pathdata = '';
216 $c = 0;
217 $p1 = null;
218 $num_colours = count($colours);
219 $points = $this->getGridPointsY($this->main_y_axis);
220 $first = array_shift($points);
221 $last_pos = $first->position;
222 $stripes = '';
223 foreach($points as $y) {
224 $y = $y->position;
225 $cc = $colours[$c % $num_colours];
226 if($cc !== null) {
227 $y1 = $last_pos - $y;
228 $dpath = new PathData('M', $xleft, $y, 'l', $xd, $yd);
229 $dpath->add('h', $x_w, 'v', $y1, 'h', $minus_x_w);
230 $dpath->add('l', $minus_xd, $minus_yd, 'z');
231 $bpath = [
232 'fill' => new Colour($this, $cc),
233 'd' => $dpath,
234 ];
235 if($this->getOption('grid_back_stripe_opacity') != 1)
236 $bpath['fill-opacity'] = $this->getOption('grid_back_stripe_opacity');
237 $stripes .= $this->element('path', $bpath);
238 } else {
239 $p1 = $y;
240 }
241 $last_pos = $y;
242 ++$c;
243 }
244 return $stripes;
245 }
246
247 /**
248 * clamps a value to the grid boundaries
249 */
250 protected function clampVertical($val)
251 {
252 return max($this->height - $this->pad_bottom - $this->g_height,
253 min($this->height - $this->pad_bottom, $val));
254 }
255
256 protected function clampHorizontal($val)
257 {
258 return max($this->width - $this->pad_right - $this->g_width,
259 min($this->width - $this->pad_right, $val));
260 }
261
262 /**
263 * Returns the path for a guideline, and sets dimensions of the straight bit
264 */
265 public function guidelinePathBelow($axis, $value, &$x, &$y, &$w, &$h,
266 $reverse_length)
267 {
268 $coords = new Coords($this);
269
270 $grid_value = 'g' . (is_numeric($value) ? new Number($value) : $value);
271 if($axis == 'x') {
272 $y = $coords->transform('gt', 'y');
273 $x = $coords->transform($grid_value, 'x', null);
274 if($x === null)
275 return '';
276
277 if(is_string($h) || $h > 0) {
278 $h = $coords->transform($h, 'y');
279 } else {
280 $h = $coords->transform('gh', 'y');
281 }
282 if(!$reverse_length)
283 $y = $coords->transform('gb', 'y') - $h;
284
285 } else {
286 $x = $coords->transform('gl', 'x');
287 $y = $coords->transform($grid_value, 'y', null);
288 if($y === null)
289 return '';
290
291 if(is_string($w) || $w > 0) {
292 $w = $coords->transform($w, 'x');
293 } else {
294 $w = $coords->transform('gw', 'x');
295 }
296 if($reverse_length)
297 $x = $coords->transform('gr', 'x') - $w;
298 $h = 0;
299 }
300
301 // get the depth section of the path
302 $z = $this->depth * $this->depth_unit;
303 list($xd, $yd) = $this->project(0, 0, $z);
304 $x1 = $x;
305 $y1 = $y + $h;
306 $x = $x + $xd;
307 $y = $y + $yd;
308
309 // for reverse lengths the line doesn't meet the axis
310 $path = new PathData('M', $x, $y, 'l', $w, $h);
311 if(!$reverse_length)
312 $path->add('M', $x1, $y1, 'l', $xd, $yd);
313 return $path;
314 }
315
316 /**
317 * Returns TRUE if the item is visible on the graph
318 */
319 public function isVisible($item, $dataset = 0)
320 {
321 // 0 values are visible, NULLs are not
322 return $item->value !== null;
323 }
324
325 /**
326 * Returns the SVG code for a bar
327 */
328 protected function drawBar3D(DataItem $item, $index, $start = 0, $axis = null,
329 $dataset = 0, $options = [])
330 {
331 if($item->value === null)
332 return '';
333
334 $bar = $this->barDimensions($item, $index, $start, $axis, $dataset);
335 if(empty($bar))
336 return '';
337
338 // is the bar top drawn?
339 $top = !isset($options['top']) || $options['top'];
340
341 // if the bar is empty and no legend or labels to show give up now
342 if(!$top && (string)$bar['height'] == '0' && !$this->getOption('legend_show_empty') &&
343 !$this->getOption('show_data_labels'))
344 return '';
345
346 // fill and stroke the group
347 $group = ['fill' => $this->getColour($item, $index, $dataset)];
348 $this->setStroke($group, $item, $index, $dataset);
349
350 if($this->getOption('semantic_classes'))
351 $group['class'] = 'series' . $dataset;
352
353 $label_shown = $this->addDataLabel($dataset, $index, $group, $item,
354 $bar['x'], $bar['y'], $bar['width'], $bar['height']);
355
356 if($this->getOption('show_tooltips'))
357 $this->setTooltip($group, $item, $dataset, $item->key, $item->value,
358 $label_shown);
359 if($this->getOption('show_context_menu'))
360 $this->setContextMenu($group, $dataset, $item, $label_shown);
361
362 $bar_part = $this->element('g', $group, null,
363 $this->bar3D($item, $bar, $top, $index, $dataset, $start, $axis));
364 return $this->getLink($item, $item->key, $bar_part);
365 }
366 }
367
368