1<?php
2/**
3 * 3d Library
4 *
5 * PHP versions 5
6 *
7 * LICENSE:
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 * @category Image
23 * @package  Image_3D
24 * @author   Arne Nordmann <3d-rotate@arne-nordmann.de>
25 */
26
27/**
28 * Creates a SVG, to move and rotate the 3D-object at runtime
29 *
30 * @category Image
31 * @package  Image_3D
32 * @author   Arne Nordmann <3d-rotate@arne-nordmann.de>
33 */
34class Image_3D_Driver_SVGControl extends Image_3D_Driver
35{
36
37    /**
38     * Width of the image
39     */
40    protected $_x;
41    /**
42     * Height of the image
43     */
44    protected $_y;
45
46    /**
47     * Current, increasing element-id (integer)
48     */
49    protected $_id;
50
51    /**
52     * Array of gradients
53     */
54    protected $_gradients;
55    /**
56     * Rectangle with background-color
57     */
58    protected $_background;
59    /**
60     * Array of polygones
61     */
62    protected $_polygones;
63
64    /**
65     * Constructor
66     *
67     */
68    public function __construct()
69    {
70        $this->_image = '';
71        $this->_id    = 1;
72
73        $this->_gradients = array();
74        $this->_polygones = array();
75    }
76
77    /**
78     * Creates image header
79     *
80     * @param float $x width of the image
81     * @param float $y height of the image
82     *
83     * @return void
84     */
85    public function createImage($x, $y)
86    {
87        $this->_x = (int) $x;
88        $this->_y = (int) $y;
89
90        $this->_image = <<<EOF
91<?xml version="1.0" ?>
92<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
93         "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
94
95<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="{$this->_x}" height="{$this->_y}" onload="init(evt)">
96EOF;
97
98        $this->_image .= "\n\n";
99    }
100
101    /**
102     * Adds coloured background to the image
103     *
104     * Draws a rectangle with the size of the image and the passed colour
105     *
106     * @param Image_3D_Color $color Background colour of the image
107     *
108     * @return void
109     */
110    public function setBackground(Image_3D_Color $color)
111    {
112        $this->_background  = "\t<!-- coloured background -->\n";
113        $this->_background .= sprintf("\t<rect id=\"background\" x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" style=\"%s\" />\n",
114            $this->_x,
115            $this->_y,
116            $this->_getStyle($color));
117    }
118
119    protected function _getStyle(Image_3D_Color $color)
120    {
121        $values = $color->getValues();
122
123        $values[0] = (int) round($values[0] * 255);
124        $values[1] = (int) round($values[1] * 255);
125        $values[2] = (int) round($values[2] * 255);
126        $values[3] = 1 - $values[3];
127
128        // optional: "shape-rendering: optimizeSpeed;" to increase speed
129        return sprintf('fill:#%02x%02x%02x; stroke:none;',
130                        $values[0],
131                        $values[1],
132                        $values[2])
133               .((empty($values[3]))?'':' opacity:'.$values[3]); // opacity
134    }
135
136    protected function _getStop(Image_3D_Color $color, $offset = 0, $alpha = null)
137    {
138        $values = $color->getValues();
139
140        $values[0] = (int) round($values[0] * 255);
141        $values[1] = (int) round($values[1] * 255);
142        $values[2] = (int) round($values[2] * 255);
143        if ($alpha === null) {
144            $values[3] = 1 - $values[3];
145        } else {
146            $values[3] = 1 - $alpha;
147        }
148
149        return sprintf("\t\t\t<stop id=\"stop%d\" offset=\"%.1f\" style=\"stop-color:rgb(%d, %d, %d); stop-opacity:%.4f;\" />\n",
150                        $this->_id++,
151                        $offset,
152                        $values[0],
153                        $values[1],
154                        $values[2],
155                        $values[3]);
156    }
157
158    protected function _addGradient($string)
159    {
160        $id = 'linearGradient' . $this->_id++;
161
162        $this->_gradients[] = str_replace('[id]', $id, $string);
163        return $id;
164    }
165
166    protected function _addPolygon($string)
167    {
168        $id = 'data_polygon' . $this->_id++;
169
170        $this->_polygones[] = str_replace('[id]', $id, $string);
171        return $id;
172    }
173
174    public function drawPolygon(Image_3D_Polygon $polygon)
175    {
176        $list   = '';
177        $points = $polygon->getPoints();
178
179        $svg = "\t\t\t<polygon id=\"[id]\" "; //Image_3D_P
180
181        // number of points
182        $svg .= 'nop="'.count($points).'" ';
183
184        // coordinates on start
185        foreach ($points as $nr => $point) {
186            $svg .= 'x'.$nr.'="'.round($point->getX()).'" ';
187            $svg .= 'y'.$nr.'="'.round($point->getY()).'" ';
188            $svg .= 'z'.$nr.'="'.round($point->getZ()).'" ';
189        }
190        $svg .= 'style="'.$this->_getStyle($polygon->getColor())."\" />\n";
191
192        $this->_addPolygon($svg);
193    }
194
195    public function drawGradientPolygon(Image_3D_Polygon $polygon)
196    {
197    }
198
199    /**
200     * Creates scripting area for moving and rotating the object
201     *
202     * @return string
203     */
204    protected function _getScript()
205    {
206        $p_count = count($this->_polygones);
207
208        // Create entire scripting area for moving and rotating the polygones
209        $script = <<<EOF
210        <!-- ECMAScript for moving and rotating -->
211        <script type="text/ecmascript"> <![CDATA[
212
213            var svgdoc;
214            var svgns;
215
216            var width, height;
217            var viewpoint, distance;
218
219            var plist, pcont, t1;
220
221            var timer;
222
223            var mov_x, mov_y, mov_z;
224            var rot_x, rot_y, rot_z;
225
226            // Some kind of constructor
227            function init(evt) {
228                // Set image dimension
229                width = {$this->_x};
230                height = {$this->_y};
231
232                // Set viewpoint and distance for perspective
233                viewpoint = (width + height) / 2;
234                distance = (width + height) / 2;
235
236                // Set path to 0
237                mov_x = 0;
238                mov_y = 0;
239                mov_z = 0;
240                rot_x = 0;
241                rot_y = 0;
242                rot_z = 0;
243
244                // Reference the SVG-Document
245                svgdoc = evt.target.ownerDocument;
246                svgns = 'http://www.w3.org/2000/svg';
247
248                // Reference list of Image_3D_Polygons
249                plist = svgdoc.getElementById('plist');
250
251                // Reference container for polygones
252                pcont = svgdoc.getElementById('pcont');
253
254                drawAllPolygones();
255            }
256
257            // Draw all polygones
258            function drawAllPolygones() {
259                // Update all polygones
260                for (i=1; i<={$p_count}; ++i) {
261                    dp = svgdoc.getElementById("data_polygon"+i);
262
263                    // Number of points for this polygon
264                    nop = parseInt(dp.getAttribute("nop"));
265
266                    // find coordinates of each point
267                    pstr = '';
268                    for (j=0; j<nop; ++j) {
269                        x = parseInt(dp.getAttribute('x'+j));
270                        y = parseInt(dp.getAttribute('y'+j));
271                        z = parseInt(dp.getAttribute('z'+j));
272
273                        // Rotation
274                        if (rot_x!=0 || rot_y!=0 || rot_z!=0) {
275                            /*if (rot_x!=0) {
276                                // rotate around the x-axis
277                            }
278
279                            if (rot_y!=0) {
280                                // rotate around the y-axis
281                            }*/
282
283                            if (rot_z!=0) {
284                                // rotate around the z-axis
285                                var b = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
286                                var phi = Math.atan2(x, y);
287                                x = b * Math.sin(phi + Math.PI/360*rot_z);
288                                y = b * Math.cos(phi + Math.PI/360*rot_z);
289                            }
290                        }
291
292                        // Calculate coordinates on screen (perspectively - viewpoint, distance: 500)
293                        x = Math.round(viewpoint * (x + mov_x) / (z + distance) + {$this->_x}) / 2;
294                        y = Math.round(viewpoint * (y + mov_y) / (z + distance) + {$this->_y}) / 2;
295
296                        // Create string of points
297                        pstr += x+','+y+' ';
298                    }
299
300                    // Manipulate the polygon if existing, otherwise create it
301                    p = svgdoc.getElementById("polygon"+i);
302                    if (p==null) {
303                        // Polygon doesn't exist, create it
304                        p = svgdoc.createElementNS(svgns, 'polygon');
305
306                        // Get style of data polygon
307                        var style = dp.getAttribute("style");
308                        p.setAttributeNS(null, "style", style);
309
310                        p.setAttributeNS(null, "id", "polygon"+i);
311                        p.setAttributeNS(null, "points", pstr);
312                        pcont.appendChild(p);
313                    } else {
314                        // Polygon exists, just update points-attribute
315                        p.setAttributeNS(null, "points", pstr);
316                    }
317                }
318            }
319
320            // Translate X
321            function move_left(steps) {
322                if (steps>0) {
323                    mov_x -= 3;
324                    drawAllPolygones();
325                    timer = setTimeout('move_left('+(steps-1)+')', 1);
326                }
327            }
328            function move_right(steps) {
329                if (steps>0) {
330                    mov_x += 3;
331                    drawAllPolygones();
332                    timer = setTimeout('move_right('+(steps-1)+')', 1);
333                }
334            }
335
336            // Translate Y
337            function move_up(steps) {
338                if (steps>0) {
339                    mov_y -= 3;
340                    drawAllPolygones();
341                    timer = setTimeout('move_up('+(steps-1)+')', 1);
342                }
343            }
344            function move_down(steps) {
345                if (steps>0) {
346                    mov_y += 3;
347                    drawAllPolygones();
348                    timer = setTimeout('move_down('+(steps-1)+')', 1);
349                }
350            }
351
352            // Translate Z (zoom)
353            function zoom_in(steps) {
354                if (steps>0) {
355                    distance -= 3;
356                    viewpoint += 1;
357                    drawAllPolygones();
358                    timer = setTimeout('zoom_in('+(steps-1)+')', 1);
359                }
360            }
361            function zoom_out(steps) {
362                if (steps>0) {
363                    distance += 3;
364                    viewpoint -= 1;
365                    drawAllPolygones();
366                    timer = setTimeout('zoom_out('+(steps-1)+')', 1);
367                }
368            }
369
370            // Rotate Z
371            function rotate_z_cw(steps) {
372                if (steps>0) {
373                    rot_z -= 6;
374                    drawAllPolygones();
375                    timer = setTimeout('rotate_z_cw('+(steps-1)+')', 1);
376                }
377            }
378            function rotate_z_ccw(steps) {
379                if (steps>0) {
380                    rot_z += 6;
381                    drawAllPolygones();
382                    timer = setTimeout('rotate_z_ccw('+(steps-1)+')', 1);
383                }
384            }
385
386            // Back to default position
387            function move_to_default() {
388                // Move to default Position
389                mov_x = 0;
390                mov_y = 0;
391                mov_z = 0;
392                rot_x = 0;
393                rot_y = 0;
394                rot_z = 0;
395
396                // Kill existing timer to stop remaining movements
397                clearTimeout(timer);
398
399                // Zoom to default position
400                viewpoint = (width + height) / 2;
401                distance = (width + height) / 2;
402
403                // Draw all polygones with reset parameters
404                drawAllPolygones();
405            }
406
407            // In- or decrease one of the three coordinates
408            function moveAllPolygones(coord, step) {
409                var p, c;
410
411                // Find all polygones
412                for (i=1; i<={$p_count}; ++i) {
413                    p = svgdoc.getElementById('polygon'+i);
414
415                    // Number of points for this polygon
416                    nop = parseInt(p.getAttribute("nop"));
417
418                    switch (coord) {
419                        case 0  :   // X
420                                    for (j=0; j<nop; ++j) {
421                                        var c = parseInt(p.getAttribute('x'+j));
422                                        p.setAttribute('x'+j, (c+step));
423                                    }
424                                 break;
425                        case 1  :   // Y
426                                    for (j=0; j<nop; ++j) {
427                                        var c = parseInt(p.getAttribute('y'+j));
428                                        p.setAttribute('y'+j, (c+step));
429                                    }
430                                 break;
431                        case 2  :   // Z
432                                    for (j=0; j<nop; ++j) {
433                                        var c = parseInt(p.getAttribute('z'+j));
434                                        p.setAttribute('z'+j, (c+step));
435                                    }
436                                 break;
437                    }
438                }
439            }
440
441        ]]> </script>
442
443EOF;
444
445        return $script;
446    }
447
448    /**
449     * Creates controls for moving and rotating the object
450     *
451     * @return string
452     */
453    protected function _getControls()
454    {
455
456        function drawArrow($x, $y, $id, $rot, $funct)
457        {
458            $arrow_points = ($x+12).','.($y+3).' '.($x+3).','.($y+8).' '.($x+12).','.($y+13);
459
460            $arrow  = "\t<g id=\"".$id.'" transform="rotate('.$rot.', '.($x+8).', '.($y+8)
461                    .')" onclick="'.$funct."\" style=\"cursor:pointer\">\n";
462            $arrow .= "\t\t<rect x=\"".$x.'" y="'.$y.'" width="16" height="16" '
463                        ." style=\"fill:#bbb; stroke:none;\" />\n";
464            $arrow .= "\t\t<rect x=\"".($x+1).'" y="'.($y+1).'" width="14" height="14" '
465                        ." style=\"fill:#ddd; stroke:none;\" />\n";
466            $arrow .= "\t\t<polygon points=\"".$arrow_points.'" '
467                        ." style=\"fill:#000; stroke:none;\" />\n";
468            $arrow .= "\t</g>\n";
469
470            return $arrow;
471        }
472
473        $x = $this->_x * 0.05;
474        $y = $this->_y * 0.05;
475
476        $controls  = "\n\t<!-- Control Elements -->\n";
477        $controls .= "\t<rect x=\"0\" y=\"0\" width=\"".$this->_x."\" height=\"20\" style=\"fill:#eee; stroke:#ddd; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
478
479        // Button "1:1"
480        $controls .= "\t<g onclick=\"move_to_default()\" style=\"cursor:pointer;\">\n";
481        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
482        $controls .= "\t\t<text x=\"10\" y=\"14\" style=\"font-size:10px; text-anchor:middle;\">1:1</text>\n";
483        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
484        $controls .= "\t</g>\n";
485
486        // Button "zoom in"
487        $controls .= "\t<g onclick=\"zoom_in(10)\" transform=\"translate(25, 0)\" style=\"cursor:pointer;\">\n";
488        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
489        $controls .= "\t\t<path style=\"fill:#999; stroke:#aaa; stroke-width:1px;\" d=\"M 5 13 L 3 17 A 2 2 0 0 0 8 17 L 11 14\"/>\n";
490        $controls .= "\t\t<circle cx=\"11\" cy=\"9\" r=\"7\" style=\"fill:#fff; stroke:#ccc; stroke-width:1px;\" />\n";
491        $controls .= "\t\t<text x=\"11\" y=\"13\" style=\"font-size:10px; text-anchor:middle;\">+</text>\n";
492        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
493        $controls .= "\t</g>\n";
494
495        // Button "zoom out"
496        $controls .= "\t<g onclick=\"zoom_out(10)\" transform=\"translate(46, 0)\" style=\"cursor:pointer;\">\n";
497        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
498        $controls .= "\t\t<path style=\"fill:#999; stroke:#aaa; stroke-width:1px;\" d=\"M 5 13 L 3 17 A 2 2 0 0 0 8 17 L 11 14\"/>\n";
499        $controls .= "\t\t<circle cx=\"11\" cy=\"9\" r=\"7\" style=\"fill:#fff; stroke:#ccc; stroke-width:1px;\" />\n";
500        $controls .= "\t\t<text x=\"10\" y=\"8\" style=\"font-size:10px; text-anchor:middle;\">_</text>\n";
501        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
502        $controls .= "\t</g>\n";
503
504        /*// Button "hand"
505        $controls .= "\t<g transform=\"translate(70, 0)\">\n";
506        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
507        $controls .= "\t\t<path d=\"M 7.95,18.08 C 8.01,15.70 5.14,15.75 4.62,12.86 C 4.09,9.934 3.76,8.16 3.76,8.16 C 3.76,8.16 5.54,7.95 5.98,9.47 C 6.42,11 7.07,12.48 7.07,12.48 C 7.07,12.48 6.12,5.12 6.16,4.68 C 6.22,4.04 6.30,3.28 6.953,3.24 C 7.64,3.19 8.06,3.66 8.26,4.51 C 8.38,5.05 8.98,8.49 8.98,8.49 C 8.98,8.49 8.66,3.41 8.84,2.98 C 9.131,2.30 9.17,1.97 9.81,1.92 C 10.45,1.88 10.94,2.60 11.05,3.07 C 11.15,3.53 11.37,8.24 11.37,8.24 C 11.37,8.24 11.55,4.08 11.81,3.49 C 12.08,2.90 12.14,2.39 12.78,2.47 C 13.42,2.56 13.80,3.19 13.74,3.87 C 13.69,4.55 13.74,9.72 13.74,9.72 C 13.74,9.72 14.30,7.12 14.45,6.63 C 14.63,6.03 15.17,5.32 15.70,5.48 C 16.13,5.61 16.27,7.01 15.95,8.52 C 15.65,9.92 15.32,11.86 15.06,12.77 C 14.79,13.68 14.36,14.38 13.77,15.17 C 13.18,15.96 12.94,17.01 12.97,18.11 C 10.73,18.16 8.71,18.07 7.95,18.07\" style=\"fill:#fff; stroke:#000; stroke-width:.5;\" />\n";
508        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
509        $controls .= "\t</g>\n";
510
511        // Button "drag"
512        $controls .= "\t<g transform=\"translate(91, 0)\">\n";
513        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
514        $controls .= "\t\t<text x=\"10\" y=\"14\" style=\"font-size:10px; text-anchor:middle;\">d</text>\n";
515        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
516        $controls .= "\t</g>\n";*/
517
518        // Button "rotate counter-clockwise"
519        $controls .= "\t<g transform=\"translate(115, 0)\" onclick=\"rotate_z_ccw(15)\" style=\"cursor:pointer;\">\n";
520        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
521        $controls .= "\t\t<path style=\"fill:none; stroke:#000; stroke-width:1px;\" d=\"M 9 16 A 6 6 0 1 0 5 10\" />\n";
522        $controls .= "\t\t<polygon style=\"fill:#000;\" points=\"2,10 8,10 5,14\" />\n";
523        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
524        $controls .= "\t</g>\n";
525
526        // Button "rotate clockwise"
527        $controls .= "\t<g transform=\"translate(136, 0)\" onclick=\"rotate_z_cw(15)\" style=\"cursor:pointer;\">\n";
528        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"fill:url(#b_off); stroke:#ccc; stroke-width:1px; shape-rendering:optimizeSpeed;\" />\n";
529        $controls .= "\t\t<path style=\"fill:none; stroke:#000; stroke-width:1px;\" d=\"M 9 16 A 6 6 0 1 1 15 10\" />\n";
530        $controls .= "\t\t<polygon style=\"fill:#000;\" points=\"12,10 18,10 15,14\" />\n";
531        $controls .= "\t\t<rect x=\"0\" y=\"0\" width=\"20\" height=\"20\" style=\"opacity:0;\" />\n";
532        $controls .= "\t</g>\n";
533
534        // Move left
535        $x = 0;
536        $y = $this->_y / 2 - 8;
537
538        $controls .= drawArrow($x, $y, 'move_left', 0, 'move_left(15)');
539
540        // Move up
541        $x = $this->_x / 2 - 8;
542        $y = 20;
543
544        $controls .= drawArrow($x, $y, 'move_up', 90, 'move_up(15)');
545
546        // Move right
547        $x = $this->_x - 16;
548        $y = $this->_y / 2 - 8;
549
550        $controls .= drawArrow($x, $y, 'move_right', 180, 'move_right(15)');
551
552        // Move down
553        $x = $this->_x / 2 - 8;
554        $y = $this->_y - 16;
555
556        $controls .= drawArrow($x, $y, 'move_down', -90, 'move_down(15)');
557
558        return $controls;
559    }
560
561    public function save($file)
562    {
563        // Start of SVG definition area
564        $this->_image .= sprintf("\t<defs id=\"defs%d\">\n", $this->_id++);
565
566        // Add scripting for moving and rotating
567        $this->_image .= $this->_getScript();
568
569        // Add gradients in case of GAUROUD-shading
570        if (count($this->_gradients)>0) {
571            $this->_image .= implode('', $this->_gradients);
572        }
573
574        // Add all polygones
575        $this->_image .= "\n\t\t<!-- polygon data elements -->\n\t\t<g id=\"plist\">\n";
576        $this->_image .= implode('', $this->_polygones);
577        $this->_image .= "\t\t</g>\n";
578
579        /***** button gradients to defs *****/
580        $this->_image .= "\t<linearGradient id=\"b_off\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n";
581        $this->_image .= "\t\t<stop offset=\"0\" stop-color=\"#eee\" />\n";
582        $this->_image .= "\t\t<stop offset=\"1\" stop-color=\"#ccc\" />\n";
583        $this->_image .= "\t</linearGradient>\n";
584        $this->_image .= "\t<linearGradient id=\"b_on\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n";
585        $this->_image .= "\t\t<stop offset=\"0\" stop-color=\"#ccc\" />\n";
586        $this->_image .= "\t\t<stop offset=\"1\" stop-color=\"#eee\" />\n";
587        $this->_image .= "\t</linearGradient>\n";
588        /**********/
589
590        // End of SVG definition area
591        $this->_image .= sprintf("\t</defs>\n\n");
592
593        // Draw background
594        $this->_image .= $this->_background;
595
596        // Create container for drawn polygones
597        $this->_image .= "\n\t<!-- Container for drawn polygones-->";
598        $this->_image .= "\n\t<g id=\"cont\">";
599        $this->_image .= "\n\t\t<g id=\"pcont\">";
600        $this->_image .= "\n\t\t</g>";
601        $this->_image .= "\n\t</g>\n";
602
603        // Add controls for moving and rotating
604        $this->_image .= $this->_getControls();
605
606        $this->_image .= "</svg>\n";
607        file_put_contents($file, $this->_image);
608    }
609
610    public function getSupportedShading()
611    {
612        return array(Image_3D_Renderer::SHADE_NO, Image_3D_Renderer::SHADE_FLAT);
613    }
614}
615
616?>
617