1<?php
2
3define('IMAGE_3D_DRIVER_ASCII_GRAY', 0.01);
4
5class Image_3D_Driver_ASCII extends Image_3D_Driver
6{
7
8    protected $_size;
9    protected $_filetype;
10    protected $_points;
11    protected $_heigth;
12    protected $_image;
13
14    protected $_charArray = array(
15        0   => ' ',
16        1   => '`',
17        2   => '\'',
18        3   => '^',
19        4   => '-',
20        5   => '`',
21        6   => '/',
22        7   => '/',
23        8   => '-',
24        9   => '\\',
25        10  => '\'',
26        11  => '\\',
27        12  => '~',
28        13  => '+',
29        14  => '+',
30        15  => '*',
31        16  => '.',
32        17  => '|',
33        18  => '/',
34        19  => '/',
35        20  => '|',
36        21  => '|',
37        22  => '/',
38        23  => '/',
39        24  => '/',
40        25  => ')',
41        26  => '/',
42        27  => 'Y',
43        28  => 'r',
44        29  => '}',
45        30  => '/',
46        31  => 'P',
47        32  => '.',
48        33  => '\\',
49        34  => '|',
50        35  => '^',
51        36  => '\\',
52        37  => '\\',
53        38  => '(',
54        39  => '(',
55        40  => ':',
56        41  => '\\',
57        42  => '|',
58        43  => 'I',
59        44  => ';',
60        45  => '\\',
61        46  => '{',
62        47  => '9',
63        48  => '_',
64        49  => '_',
65        50  => '_',
66        51  => 'C',
67        52  => '<',
68        53  => 'L',
69        54  => 'l',
70        55  => 'C',
71        56  => '>',
72        57  => 'J',
73        58  => 'J',
74        59  => 'J',
75        60  => 'o',
76        61  => 'b',
77        62  => 'd',
78        63  => '#',
79    );
80
81    public function __construct()
82    {
83        parent::__construct();
84        $this->_filetype = 'txt';
85
86        $this->reset();
87    }
88
89    public function reset()
90    {
91        $this->_points = array();
92        $this->_heigth = array();
93
94        $this->_image = array();
95    }
96
97    public function createImage($x, $y)
98    {
99        $this->_size = array($x, $y);
100    }
101
102    protected function _getColor(Image_3D_Color $color, $alpha = 1.)
103    {
104        $values = $color->getValues();
105        return array($values[0], $values[1], $values[2], (1 - $values[3]) * $alpha);
106    }
107
108    protected function _mixColor($old, $new)
109    {
110        $faktor = (1 - $new[3]) * $old[3]; // slight speed improvement
111        return array(
112            $old[0] * $faktor + $new[0] * $new[3],
113            $old[1] * $faktor + $new[1] * $new[3],
114            $old[2] * $faktor + $new[2] * $new[3],
115            $old[3] * $old[3] + $new[3]
116        );
117
118    }
119
120    public function setBackground(Image_3D_Color $color)
121    {
122        $bg = $this->_getColor($color);
123
124        for ($x = 0; $x < $this->_size[0]; ++$x) {
125            for ($y = 0; $y < $this->_size[1]; ++$y) {
126                $this->_image[$x][$y] = $bg;
127            }
128        }
129    }
130
131    protected function _drawLine(Image_3D_Point $p1, Image_3D_Point $p2)
132    {
133        list($x1, $y1) = $p1->getScreenCoordinates();
134        list($x2, $y2) = $p2->getScreenCoordinates();
135
136        $steps = ceil(max(abs($x1 - $x2), abs($y1 - $y2)));
137
138        $xdiff = ($x2 - $x1) / $steps;
139        $ydiff = ($y2 - $y1) / $steps;
140
141        $points = array();
142        for ($i = 0; $i < $steps; ++$i) {
143            $points[(int) round($x1 + $i * $xdiff)][(int) round($y1 + $i * $ydiff)] = true;
144        }
145        return $points;
146    }
147
148    protected function _getPolygonOutlines($pointArray)
149    {
150        $map = array();
151
152        $last = end($pointArray);
153        foreach ($pointArray as $point) {
154            $line = $this->_drawLine($last, $point);
155            $last = $point;
156            // Merge line to map
157            foreach ($line as $x => $row) {
158                foreach ($row as $y => $height) {
159                    $map[(int) $x][(int) $y] = $height;
160                }
161            }
162        }
163
164        return $map;
165    }
166
167    public function drawPolygon(Image_3D_Polygon $polygon)
168    {
169        $points = $this->_getPolygonOutlines($polygon->getPoints());
170
171        foreach ($points as $x => $row) {
172            if (count($row) < 2) {
173                continue;
174            }
175
176            $start = min(array_keys($row));
177            $end   = max(array_keys($row));
178
179            // Starting point
180            $this->_heigth[$x][$start] = $this->_getColor($polygon->getColor());
181
182            // the way between
183            for ($y = $start + 1; $y < $end; ++$y) {
184                $this->_heigth[$x][$y] = $this->_getColor($polygon->getColor());
185            }
186
187            // Ending point
188            $this->_points[$x][$end] = $this->_getColor($polygon->getColor());
189        }
190    }
191
192    public function drawGradientPolygon(Image_3D_Polygon $polygon)
193    {
194        $this->drawPolygon($polygon);
195    }
196
197    public function setFiletye($type)
198    {
199        $type = strtolower($type);
200        if (in_array($type, array('png', 'jpeg'))) {
201            $this->_filetype = $type;
202            return true;
203        } else {
204            return false;
205        }
206    }
207
208    public function _getAnsiColorCode($color, $last = '')
209    {
210        $code = "\033[0;" . (30 + bindec((int) round($color[2]) . (int) round($color[1]) . (int) round($color[0]))) . 'm';
211        if ($last !== $code) {
212            return $code;
213        }
214        return '';
215    }
216
217    public function save($file)
218    {
219
220        $asciiWidth  = (int) ceil($this->_size[0] / 2);
221        $asciiHeight = (int) ceil($this->_size[1] / 6);
222
223        $output    = "\033[2J";
224        $lastColor = '';
225
226        for ($y = 0; $y < $asciiHeight; ++$y) {
227            for ($x = 0; $x < $asciiWidth; ++$x) {
228                // Get pixelarray
229                $char = 0;
230
231                $charColor = array(0, 0, 0);
232                for ($xi = 0; $xi < 2; ++$xi) {
233                    for ($yi = 0; $yi < 3; ++$yi) {
234                        $xPos = $x * 2 + $xi;
235                        $yPos = $y * 6 + $yi;
236
237                        if (isset($this->_heigth[$xPos][$yPos])) {
238                            $color = $this->_mixColor($this->_image[$xPos][$yPos],
239                            $this->_heigth[$xPos][$yPos]);
240                            if ((($color[0] + $color[1] + $color[2]) / 3) > IMAGE_3D_DRIVER_ASCII_GRAY) {
241                                $char |= pow(2, $yi + ($xi * 3));
242                            }
243                            $charColor[0] += $color[0];
244                            $charColor[1] += $color[1];
245                            $charColor[2] += $color[2];
246                        }
247                    }
248                }
249                $lastColor = $this->_getAnsiColorCode(array($charColor[0] / 6, $charColor[1] / 6, $charColor[2] / 6), $lastColor);
250                $output   .= $lastColor . $this->_charArray[$char];
251            }
252            $lastColor = '';
253            $output   .= "\n";
254        }
255        $fp = fopen($file, 'w');
256        fwrite($fp, $output);
257        fclose($fp);
258    }
259
260    public function getSupportedShading()
261    {
262        return array(Image_3D_Renderer::SHADE_NO,
263                        Image_3D_Renderer::SHADE_FLAT);
264    }
265}
266
267?>
268