1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * 3d Library
7 *
8 * PHP versions 5
9 *
10 * LICENSE:
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24 *
25 * @category  Image
26 * @package   Image_3D
27 * @author    Kore Nordmann <3d@kore-nordmann.de>
28 * @copyright 1997-2005 Kore Nordmann
29 * @license   http://www.gnu.org/licenses/lgpl.txt lgpl 2.1
30 * @version   CVS: $Id$
31 * @link      http://pear.php.net/package/PackageName
32 * @since     File available since Release 0.1.0
33 */
34
35require_once 'Image/3D/Paintable.php';
36require_once 'Image/3D/Enlightenable.php';
37
38require_once 'Image/3D/Color.php';
39require_once 'Image/3D/Coordinate.php';
40require_once 'Image/3D/Point.php';
41require_once 'Image/3D/Vector.php';
42require_once 'Image/3D/Line.php';
43require_once 'Image/3D/Renderer.php';
44require_once 'Image/3D/Driver.php';
45
46require_once 'Image/3D/Paintable/Object.php';
47require_once 'Image/3D/Paintable/Light.php';
48require_once 'Image/3D/Paintable/Polygon.php';
49
50// {{{ Image_3D
51
52/**
53 * Image_3D
54 *
55 * Class for creation of 3d images only with native PHP.
56 *
57 * @category  Image
58 * @package   Image_3D
59 * @author    Kore Nordmann <3d@kore-nordmann.de>
60 * @copyright 1997-2005 Kore Nordmann
61 * @license   http://www.gnu.org/licenses/lgpl.txt lgpl 2.1
62 * @version   Release: @package_version@
63 * @link      http://pear.php.net/package/PackageName
64 * @since     Class available since Release 0.1.0
65 */
66class Image_3D
67{
68
69    // {{{ properties
70
71    /**
72     * Backgroundcolor
73     *
74     * @var Image_3D_Color
75     */
76    protected $_color;
77
78    /**
79     * List of known objects
80     *
81     * @var array
82     */
83    protected $_objects;
84
85    /**
86     * List of lights
87     *
88     * @var array
89     */
90    protected $_lights;
91
92    /**
93     * Active renderer
94     *
95     * @var Image_3D_Renderer
96     */
97    protected $_renderer;
98
99    /**
100     * Active outputdriver
101     *
102     * @var Image_3D_Driver
103     */
104    protected $_driver;
105
106
107    /**
108     * Options for rendering
109     */
110    protected $_option;
111
112    /**
113     * Options set by the user
114     *
115     * @var array
116     */
117    protected $_optionSet;
118
119    /**
120     * Begin of world creation
121     *
122     * @var float
123     */
124    protected $_start;
125
126    // }}}
127    // {{{ constants
128
129    /**
130     * Option for filled polygones (depreceated)
131     */
132    const IMAGE_3D_OPTION_FILLED = 1;
133
134    /**
135     * Option for backface culling (depreceated)
136     */
137    const IMAGE_3D_OPTION_BF_CULLING = 2;
138
139    // }}}
140    // {{{ __construct()
141
142    /**
143     * Constructor for Image_3D
144     *
145     * Initialises the environment
146     *
147     * @return  Image_3D                World instance
148     */
149    public function __construct()
150    {
151        $this->_objects  = array();
152        $this->_lights   = array();
153        $this->_renderer = null;
154        $this->_driver   = null;
155        $this->_color    = null;
156
157        $this->_option[self::IMAGE_3D_OPTION_FILLED]     = true;
158        $this->_option[self::IMAGE_3D_OPTION_BF_CULLING] = true;
159
160        $this->_optionSet = array();
161
162        $this->_start = microtime(true);
163    }
164
165    // }}}
166    // {{{ createObject()
167
168    /**
169     * Factory method for Objects
170     *
171     * Creates and returns a printable object.
172     * Standard objects with parameters:
173     *     - cube        array(float $x, float $y, float $z)
174     *     - sphere    array(float $r, int $detail)
175     *     - 3ds        string $filename
176     *     - map        [array(array(Image_3D_Point))]
177     *     - text        string $string
178     *
179     * @param string $type      Objectname
180     * @param array  $parameter Parameters
181     *
182     * @return  Image_3D_Object            Object instance
183     */
184    public function createObject($type, $parameter = array())
185    {
186        $name  = ucfirst($type);
187        $class = 'Image_3D_Object_' . $name;
188
189        $absolute_path = dirname(__FILE__) . '/3D/Paintable/Object/' . $name . '.php';
190        $user_path     = dirname(__FILE__) . '/3D/User/Object/' . $name . '.php';
191
192        if (is_file($absolute_path) && is_readable($absolute_path)) {
193            include_once $absolute_path;
194        } elseif (is_file($user_path) && is_readable($user_path)) {
195            include_once $user_path;
196        } else {
197            throw new Exception("Class for object $name not found (Searched: $absolute_path, $user_path).");
198        }
199
200        return $this->_objects[] = new $class($parameter);
201    }
202
203    // }}}
204    // {{{ createLight()
205
206    /**
207     * Factory method for lights
208     *
209     * Creates and returns a light. Needs only the position of the lights as a
210     * parameter.
211     *
212     * @param string $type      Class
213     * @param mixed  $parameter Parameters
214     *
215     * @return  Image_3D_Light          Object instance
216     */
217    public function createLight($type, $parameter = array())
218    {
219        $name = ucfirst($type);
220        if ($name != 'Light') {
221            $class = 'Image_3D_Light_' . $name;
222
223            $absolute_path = dirname(__FILE__) . '/3D/Paintable/Light/' . $name . '.php';
224            $user_path     = dirname(__FILE__) . '/3D/User/Light/' . $name . '.php';
225
226            if (is_file($absolute_path) && is_readable($absolute_path)) {
227                include_once $absolute_path;
228            } elseif (is_file($user_path) && is_readable($user_path)) {
229                include_once $user_path;
230            } else {
231                throw new Exception("Class for object $name not found (Searched: $absolute_path, $user_path).");
232            }
233
234            return $this->_lights[] = new $class($parameter[0], $parameter[1], $parameter[2], array_slice($parameter, 3));
235        } else {
236            return $this->_lights[] = new Image_3D_Light($parameter[0], $parameter[1], $parameter[2]);
237        }
238    }
239
240    // }}}
241    // {{{ createMatrix()
242
243    /**
244     * Factory method for transformation matrixes
245     *
246     * Creates a transformation matrix
247     * Known matrix types:
248     *  - rotation      array(float $x, float $y, float $z)
249     *  - scale         array(float $x, float $y, float $z)
250     *  - move          array(float $x, float $y, float $z)
251     *
252     * @param string $type      Matrix type
253     * @param array  $parameter Parameters
254     *
255     * @return  Image_3D_Matrix         Object instance
256     */
257    public function createMatrix($type, $parameter = array())
258    {
259        $name  = ucfirst($type);
260        $class = 'Image_3D_Matrix_' . $name;
261
262        $absolute_path = dirname(__FILE__) . '/3D/Matrix/' . $name . '.php';
263
264        if (is_file($absolute_path) && is_readable($absolute_path)) {
265            include_once $absolute_path;
266        } else {
267            throw new Exception("Class for matrix $name not found (Searched: $absolute_path, $user_path).");
268        }
269
270        return new $class($parameter);
271    }
272
273    // }}}
274    // {{{ setColor()
275
276    /**
277     * Sets world backgroundcolor
278     *
279     * Sets the backgroundcolor for final image. Transparancy is not supported
280     * by all drivers
281     *
282     * @param Image_3D_Color $color Backgroundcolor
283     *
284     * @return  void
285     */
286    public function setColor(Image_3D_Color $color)
287    {
288        $this->_color = $color;
289    }
290
291    // }}}
292    // {{{ createRenderer()
293
294    /**
295     * Factory method for renderer
296     *
297     * Creates and returns a renderer.
298     * Avaible renderers
299     *  - Isometric
300     *  - Perspektively
301     *
302     * @param string $type Renderer type
303     *
304     * @return  Image_3D_Renderer       Object instance
305     */
306    public function createRenderer($type)
307    {
308        $name  = ucfirst($type);
309        $class = 'Image_3D_Renderer_' . $name;
310
311        $absolute_path = dirname(__FILE__) . '/3D/Renderer/' . $name . '.php';
312        $user_path     = dirname(__FILE__) . '/3D/User/Renderer/' . $name . '.php';
313
314        if (is_file($absolute_path) && is_readable($absolute_path)) {
315            include_once $absolute_path;
316        } elseif (is_file($user_path) && is_readable($user_path)) {
317            include_once $user_path;
318        } else {
319            throw new Exception("Class for renderer $name not found (Searched: $absolute_path, $user_path).");
320        }
321
322        return $this->_renderer = new $class();
323    }
324
325    // }}}
326    // {{{ createDriver()
327
328    /**
329     * Factory method for drivers
330     *
331     * Creates and returns a new driver
332     * Standrad available drivers:
333     *  - GD
334     *  - SVG
335     *
336     * @param string $type Driver type
337     *
338     * @return  Image_3D_Driver         Object instance
339     */
340    public function createDriver($type)
341    {
342        $name  = ucfirst($type);
343        $class = 'Image_3D_Driver_' . $name;
344
345        $absolute_path = dirname(__FILE__) . '/3D/Driver/' . $name . '.php';
346        $user_path     = dirname(__FILE__) . '/3D/User/Driver/' . $name . '.php';
347
348        if (is_file($absolute_path) && is_readable($absolute_path)) {
349            include_once $absolute_path;
350        } elseif (is_file($user_path) && is_readable($user_path)) {
351            include_once $user_path;
352        } else {
353            throw new Exception("Class for driver $name not found (Searched: $absolute_path, $user_path).");
354        }
355
356        return $this->_driver = new $class();
357    }
358
359    // }}}
360    // {{{ setOption()
361
362    /**
363     * Sets an option for all known objects
364     *
365     * Sets one of the Image_3D options for all known objects
366     *
367     * @param integer $option Option
368     * @param mixed   $value  Value
369     *
370     * @return  void
371     */
372    public function setOption($option, $value)
373    {
374        $this->_option[$option]    = $value;
375        $this->_optionSet[$option] = true;
376
377        foreach ($this->_objects as $object) {
378            $object->setOption($option, $value);
379        }
380    }
381
382    // }}}
383    // {{{ transform()
384
385    /**
386     * Transform all known objects
387     *
388     * Transform all known objects with the given transformation matrix.
389     * Can be interpreted as a transformation of the viewpoint.
390     *
391     * The id is an optional value which shouldn't be set by the user to
392     * avoid double calculations, if a point is related to more than one
393     * object.
394     *
395     * @param Image_3D_Matrix $matrix Transformation matrix
396     * @param mixed           $id     Transformation ID
397     *
398     * @return  void
399     */
400    public function transform(Image_3D_Matrix $matrix, $id = null)
401    {
402
403        if ($id === null) {
404            $id = substr(md5(microtime()), 0, 8);
405        }
406
407        foreach ($this->_objects as $object) {
408            $object->transform($matrix, $id);
409        }
410    }
411
412    // }}}
413    // {{{ render()
414
415    /**
416     * Renders the image
417     *
418     * Starts rendering an image with given size into the given file.
419     *
420     * @param integer $x    Width
421     * @param integer $y    Height
422     * @param string  $file Filename
423     *
424     * @return  boolean Success
425     */
426    public function render($x, $y, $file)
427    {
428        // Hack because stdout is not writeable
429        if ((is_file($file) || !is_writeable(dirname($file)))
430            &&    (!is_file($file) || !is_writeable($file))
431            &&    !preg_match('/^\s*php:\/\/(stdout|output)\s*$/i', $file)) {
432            throw new Exception('Cannot write outputfile.');
433        }
434
435        $x = min(1280, max(0, (int) $x));
436        $y = min(1280, max(0, (int) $y));
437
438        $this->_renderer->setSize($x, $y);
439        $this->_renderer->setBackgroundColor($this->_color);
440        $this->_renderer->addObjects($this->_objects);
441        $this->_renderer->addLights($this->_lights);
442        $this->_renderer->setDriver($this->_driver);
443
444        return $this->_renderer->render($file);
445    }
446
447    // }}}
448    // {{{ stats()
449
450    /**
451     * Statistics for Image_3D
452     *
453     * Returns simple statisics for Image_3D as a string.
454     *
455     * @return  string                  Statistics
456     */
457    public function stats()
458    {
459        return sprintf('Image 3D
460
461objects:    %d
462lights:     %d
463polygones:  %d
464points:     %d
465
466time:       %.4f s
467',
468            count($this->_objects),
469            $this->_renderer->getLightCount(),
470            $this->_renderer->getPolygonCount(),
471            $this->_renderer->getPointCount(),
472            microtime(true) - $this->_start);
473    }
474
475    // }}}
476}
477
478// }}}
479