1<?php
2
3class Image_3D_Driver_ZBuffer extends Image_3D_Driver
4{
5
6    protected $_filetype;
7    protected $_points;
8    protected $_heigth;
9
10    public function __construct()
11    {
12        parent::__construct();
13
14        $this->_filetype = 'png';
15
16        $this->_points = array();
17        $this->_heigth = array();
18    }
19
20    public function createImage($x, $y)
21    {
22        $this->_image = imagecreatetruecolor($x, $y);
23        imagealphablending($this->_image, true);
24        imageSaveAlpha($this->_image, true);
25    }
26
27    protected function _getColor(Image_3D_Color $color, $alpha = 1.)
28    {
29        $values = $color->getValues();
30
31        $values[0] = (int) round($values[0] * 255);
32        $values[1] = (int) round($values[1] * 255);
33        $values[2] = (int) round($values[2] * 255);
34        $values[3] = (int) round((1 - ((1 - $values[3]) * $alpha)) * 127);
35
36        if ($values[3] > 0) {
37            // Tranzparente Farbe allokieren
38            $color = imageColorExactAlpha($this->_image, $values[0], $values[1], $values[2], $values[3]);
39            if ($color === -1) {
40                // Wenn nicht Farbe neu alloziieren
41                $color = imageColorAllocateAlpha($this->_image, $values[0], $values[1], $values[2], $values[3]);
42            }
43        } else {
44            // Deckende Farbe allozieren
45            $color = imageColorExact($this->_image, $values[0], $values[1], $values[2]);
46            if ($color === -1) {
47                // Wenn nicht Farbe neu alloziieren
48                $color = imageColorAllocate($this->_image, $values[0], $values[1], $values[2]);
49            }
50        }
51
52        return $color;
53    }
54
55    public function setBackground(Image_3D_Color $color)
56    {
57        $bg = $this->_getColor($color);
58        imagefill($this->_image, 1, 1, $bg);
59    }
60
61    protected function _drawLine(Image_3D_Point $p1, Image_3D_Point $p2)
62    {
63        list($x1, $y1) = $p1->getScreenCoordinates();
64        list($x2, $y2) = $p2->getScreenCoordinates();
65
66        $z1 = $p1->getZ();
67        $z2 = $p2->getZ();
68
69        $steps = ceil(max(abs($x1 - $x2), abs($y1 - $y2)));
70
71        $xdiff = ($x2 - $x1) / $steps;
72        $ydiff = ($y2 - $y1) / $steps;
73        $zdiff = ($z2 - $z1) / $steps;
74
75        $points = array('height' => array(), 'coverage' => array());
76        for ($i = 0; $i < $steps; $i++) {
77            $x = $x1 + $i * $xdiff;
78
79            $xFloor  = floor($x);
80            $xCeil   = ceil($x);
81            $xOffset = $x - $xFloor;
82
83            $y = $y1 + $i * $ydiff;
84
85            $yFloor  = floor($y);
86            $yCeil   = ceil($y);
87            $yOffset = $y - $yFloor;
88
89            if (!isset($points['coverage'][(int) $xFloor][(int) $yCeil])) {
90                $points['height'][(int) $xFloor][(int) $yCeil]   = $z1 + $i * $zdiff;
91                $points['coverage'][(int) $xFloor][(int) $yCeil] = (1 - $xOffset) * $yOffset;
92            } else {
93                $points['coverage'][(int) $xFloor][(int) $yCeil] += (1 - $xOffset) * $yOffset;
94            }
95
96            if (!isset($points['coverage'][(int) $xFloor][(int) $yFloor])) {
97                $points['height'][(int) $xFloor][(int) $yFloor]   = $z1 + $i * $zdiff;
98                $points['coverage'][(int) $xFloor][(int) $yFloor] = (1 - $xOffset) * (1 - $yOffset);
99            } else {
100                $points['coverage'][(int) $xFloor][(int) $yFloor] += (1 - $xOffset) * (1 - $yOffset);
101            }
102
103            if (!isset($points['coverage'][(int) $xCeil][(int) $yCeil])) {
104                $points['height'][(int) $xCeil][(int) $yCeil]   = $z1 + $i * $zdiff;
105                $points['coverage'][(int) $xCeil][(int) $yCeil] = $xOffset * $yOffset;
106            } else {
107                $points['coverage'][(int) $xCeil][(int) $yCeil] += $xOffset * $yOffset;
108            }
109
110            if (!isset($points['coverage'][(int) $xCeil][(int) $yFloor])) {
111                $points['height'][(int) $xCeil][(int) $yFloor]   = $z1 + $i * $zdiff;
112                $points['coverage'][(int) $xCeil][(int) $yFloor] = $xOffset * (1 - $yOffset);
113            } else {
114                $points['coverage'][(int) $xCeil][(int) $yFloor] += $xOffset * (1 - $yOffset);
115            }
116        }
117        return $points;
118    }
119
120    protected function _getPolygonOutlines($pointArray)
121    {
122        $map = array('height' => array(), 'coverage' => array());
123
124        $last = end($pointArray);
125        foreach ($pointArray as $point) {
126            $line = $this->_drawLine($last, $point);
127            $last = $point;
128            // Merge line to map
129            foreach ($line['height'] as $x => $row) {
130                foreach ($row as $y => $height) {
131                    $map['height'][(int) $x][(int) $y]   = $height;
132                    $map['coverage'][(int) $x][(int) $y] = $line['coverage'][(int) $x][(int) $y];
133                }
134            }
135        }
136
137        return $map;
138    }
139
140    public function drawPolygon(Image_3D_Polygon $polygon)
141    {
142        $points = $this->_getPolygonOutlines($polygon->getPoints());
143
144        foreach ($points['coverage'] as $x => $row) {
145            if (count($row) < 2) {
146                continue;
147            }
148
149            $start = min(array_keys($row));
150            $end   = max(array_keys($row));
151
152            $zStart = $points['height'][$x][$start];
153            $zEnd   = $points['height'][$x][$end];
154            $zStep  = ($zEnd - $zStart) / ($end - $start);
155
156            // Starting point
157            $this->_heigth[$x][$start][(int) ($zStart * 100)] = $this->_getColor($polygon->getColor(), $points['coverage'][$x][$start]);
158
159            // the way between
160            for ($y = $start + 1; $y < $end; $y++) {
161                $this->_heigth[$x][$y][(int) (($zStart + $zStep * ($y - $start)) * 100)] = $this->_getColor($polygon->getColor());
162            }
163
164            // Ending point
165            $this->_points[$x][$end][(int) ($zEnd * 100)] = $this->_getColor($polygon->getColor(), $points['coverage'][$x][$end]);
166        }
167    }
168
169    public function drawGradientPolygon(Image_3D_Polygon $polygon)
170    {
171        $this->drawPolygon($polygon);
172    }
173
174    public function setFiletye($type)
175    {
176        $type = strtolower($type);
177        if (in_array($type, array('png', 'jpeg'))) {
178            $this->_filetype = $type;
179            return true;
180        } else {
181            return false;
182        }
183    }
184
185    public function save($file)
186    {
187
188        foreach ($this->_heigth as $x => $row) {
189            foreach ($row as $y => $points) {
190                krsort($points);
191                foreach ($points as $color) {
192                    imagesetpixel($this->_image, $x, $y, $color);
193                }
194            }
195        }
196
197        switch ($this->_filetype) {
198        case 'png':
199            return imagepng($this->_image, $file);
200        case 'jpeg':
201            return imagejpeg($this->_image, $file);
202        }
203    }
204
205    public function getSupportedShading()
206    {
207        return array(Image_3D_Renderer::SHADE_NO,
208                     Image_3D_Renderer::SHADE_FLAT);
209    }
210}
211
212?>
213