1 /*
2  *  libcaca       Colour ASCII-Art library
3  *  Copyright (c) 2002-2012 Sam Hocevar <sam@hocevar.net>
4  *                All Rights Reserved
5  *
6  *  This library is free software. It comes without any warranty, to
7  *  the extent permitted by applicable law. You can redistribute it
8  *  and/or modify it under the terms of the Do What the Fuck You Want
9  *  to Public License, Version 2, as published by Sam Hocevar. See
10  *  http://www.wtfpl.net/ for more details.
11  */
12 
13 /*
14  *  This file contains ellipse and circle drawing functions, both filled
15  *  and outline.
16  */
17 
18 #include "config.h"
19 
20 #if !defined(__KERNEL__)
21 #   include <stdlib.h>
22 #endif
23 
24 #include "caca.h"
25 #include "caca_internals.h"
26 
27 static void ellipsepoints(caca_canvas_t *, int, int, int, int, uint32_t, int);
28 
29 /** \brief Draw a circle on the canvas using the given character.
30  *
31  *  This function never fails.
32  *
33  *  \param cv The handle to the libcaca canvas.
34  *  \param x Center X coordinate.
35  *  \param y Center Y coordinate.
36  *  \param r Circle radius.
37  *  \param ch UTF-32 character to be used to draw the circle outline.
38  *  \return This function always returns 0.
39  */
caca_draw_circle(caca_canvas_t * cv,int x,int y,int r,uint32_t ch)40 int caca_draw_circle(caca_canvas_t *cv, int x, int y, int r, uint32_t ch)
41 {
42     int test, dx, dy;
43 
44     /* Optimized Bresenham. Kick ass. */
45     for(test = 0, dx = 0, dy = r ; dx <= dy ; dx++)
46     {
47         ellipsepoints(cv, x, y, dx, dy, ch, 1);
48         ellipsepoints(cv, x, y, dy, dx, ch, 1);
49 
50         test += test > 0 ? dx - dy-- : dx;
51     }
52 
53     return 0;
54 }
55 
56 /** \brief Fill an ellipse on the canvas using the given character.
57  *
58  *  This function never fails.
59  *
60  *  \param cv The handle to the libcaca canvas.
61  *  \param xo Center X coordinate.
62  *  \param yo Center Y coordinate.
63  *  \param a Ellipse X radius.
64  *  \param b Ellipse Y radius.
65  *  \param ch UTF-32 character to be used to fill the ellipse.
66  *  \return This function always returns 0.
67  */
caca_fill_ellipse(caca_canvas_t * cv,int xo,int yo,int a,int b,uint32_t ch)68 int caca_fill_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b,
69                        uint32_t ch)
70 {
71     int d2;
72     int x = 0;
73     int y = b;
74     int d1 = b*b - (a*a*b) + (a*a/4);
75 
76     while(a*a*y - a*a/2 > b*b*(x+1))
77     {
78         if(d1 < 0)
79         {
80             d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */
81         }
82         else
83         {
84             d1 += b*b*(2*x*1) + a*a*(-2*y+2);
85             caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch);
86             caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch);
87             y--;
88         }
89         x++;
90     }
91 
92     caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch);
93     caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch);
94 
95     d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b;
96     while(y > 0)
97     {
98         if(d2 < 0)
99         {
100             d2 += b*b*(2*x+2) + a*a*(-2*y+3);
101             x++;
102         }
103         else
104         {
105             d2 += a*a*(-2*y+3);
106         }
107 
108         y--;
109         caca_draw_line(cv, xo - x, yo - y, xo + x, yo - y, ch);
110         caca_draw_line(cv, xo - x, yo + y, xo + x, yo + y, ch);
111     }
112 
113     return 0;
114 }
115 
116 /** \brief Draw an ellipse on the canvas using the given character.
117  *
118  *  This function never fails.
119  *
120  *  \param cv The handle to the libcaca canvas.
121  *  \param xo Center X coordinate.
122  *  \param yo Center Y coordinate.
123  *  \param a Ellipse X radius.
124  *  \param b Ellipse Y radius.
125  *  \param ch UTF-32 character to be used to draw the ellipse outline.
126  *  \return This function always returns 0.
127  */
caca_draw_ellipse(caca_canvas_t * cv,int xo,int yo,int a,int b,uint32_t ch)128 int caca_draw_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b,
129                        uint32_t ch)
130 {
131     int d2;
132     int x = 0;
133     int y = b;
134     int d1 = b*b - (a*a*b) + (a*a/4);
135 
136     ellipsepoints(cv, xo, yo, x, y, ch, 0);
137 
138     while(a*a*y - a*a/2 > b*b*(x+1))
139     {
140         if(d1 < 0)
141         {
142             d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */
143         }
144         else
145         {
146             d1 += b*b*(2*x*1) + a*a*(-2*y+2);
147             y--;
148         }
149         x++;
150         ellipsepoints(cv, xo, yo, x, y, ch, 0);
151     }
152 
153     d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b;
154     while(y > 0)
155     {
156         if(d2 < 0)
157         {
158             d2 += b*b*(2*x+2) + a*a*(-2*y+3);
159             x++;
160         }
161         else
162         {
163             d2 += a*a*(-2*y+3);
164         }
165 
166         y--;
167         ellipsepoints(cv, xo, yo, x, y, ch, 0);
168     }
169 
170     return 0;
171 }
172 
173 /** \brief Draw a thin ellipse on the canvas.
174  *
175  *  This function never fails.
176  *
177  *  \param cv The handle to the libcaca canvas.
178  *  \param xo Center X coordinate.
179  *  \param yo Center Y coordinate.
180  *  \param a Ellipse X radius.
181  *  \param b Ellipse Y radius.
182  *  \return This function always returns 0.
183  */
caca_draw_thin_ellipse(caca_canvas_t * cv,int xo,int yo,int a,int b)184 int caca_draw_thin_ellipse(caca_canvas_t *cv, int xo, int yo, int a, int b)
185 {
186     /* FIXME: this is not correct */
187     int d2;
188     int x = 0;
189     int y = b;
190     int d1 = b*b - (a*a*b) + (a*a/4);
191 
192     ellipsepoints(cv, xo, yo, x, y, '-', 1);
193 
194     while(a*a*y - a*a/2 > b*b*(x+1))
195     {
196         if(d1 < 0)
197         {
198             d1 += b*b*(2*x+1); /* XXX: "Computer Graphics" has + 3 here. */
199             ellipsepoints(cv, xo, yo, x + 1, y, '0', 1);
200         }
201         else
202         {
203             d1 += b*b*(2*x*1) + a*a*(-2*y+2);
204             y--;
205             ellipsepoints(cv, xo, yo, x + 1, y, '1', 1);
206         }
207         x++;
208 
209 
210     }
211 
212     d2 = b*b*(x+0.5)*(x+0.5) + a*a*(y-1)*(y-1) - a*a*b*b;
213     while(y > 0)
214     {
215         if(d2 < 0)
216         {
217             d2 += b*b*(2*x+2) + a*a*(-2*y+3);
218             x++;
219             ellipsepoints(cv, xo, yo, x , y - 1, '2', 1);
220         }
221         else
222         {
223             d2 += a*a*(-2*y+3);
224             ellipsepoints(cv, xo, yo, x , y - 1, '3', 1);
225         }
226 
227         y--;
228 
229 
230     }
231 
232     return 0;
233 }
234 
ellipsepoints(caca_canvas_t * cv,int xo,int yo,int x,int y,uint32_t ch,int thin)235 static void ellipsepoints(caca_canvas_t *cv, int xo, int yo, int x, int y,
236                           uint32_t ch, int thin)
237 {
238     uint8_t b = 0;
239 
240     if(xo + x >= 0 && xo + x < (int)cv->width)
241         b |= 0x1;
242     if(xo - x >= 0 && xo - x < (int)cv->width)
243         b |= 0x2;
244     if(yo + y >= 0 && yo + y < (int)cv->height)
245         b |= 0x4;
246     if(yo - y >= 0 && yo - y < (int)cv->height)
247         b |= 0x8;
248 
249     if((b & (0x1|0x4)) == (0x1|0x4)) {
250         uint32_t c = ch;
251 
252         if(thin) {
253             switch(c) {
254             case '0':
255                 c = '-';
256                 break;
257             case '1':
258                 c = ',';
259                 break;
260             case '2':
261                 c = '/';
262                 break;
263             case '3':
264                 c = '|';
265                 break;
266             }
267 
268         }
269         caca_put_char(cv, xo + x, yo + y, c);
270     }
271     if((b & (0x2|0x4)) == (0x2|0x4)) {
272         uint32_t c = ch;
273 
274         if(thin) {
275             switch(c) {
276             case '0':
277                 c = '-';
278                 break;
279             case '1':
280                 c = '.';
281                 break;
282             case '2':
283                 c = '\\';
284                 break;
285             case '3':
286                 c = '|';
287                 break;
288             }
289 
290         }
291         caca_put_char(cv, xo - x, yo + y, c);
292     }
293 
294 
295     if((b & (0x1|0x8)) == (0x1|0x8)) {
296         uint32_t c = ch;
297 
298         if(thin) {
299             switch(c) {
300             case '0':
301                 c = '-';
302                 break;
303             case '1':
304                 c = '`';
305                 break;
306             case '2':
307                 c = '\\';
308                 break;
309             case '3':
310                 c = '|';
311                 break;
312             }
313 
314         }
315         caca_put_char(cv, xo + x, yo - y, c);
316     }
317 
318     if((b & (0x2|0x8)) == (0x2|0x8)) {
319         uint32_t c = ch;
320 
321         if(thin) {
322             switch(c) {
323             case '0':
324                 c = '-';
325                 break;
326             case '1':
327                 c = '\'';
328                 break;
329             case '2':
330                 c = '/';
331                 break;
332             case '3':
333                 c = '|';
334                 break;
335             }
336 
337         }
338         caca_put_char(cv, xo - x, yo - y, c);
339     }
340 }
341 
342 /*
343  * XXX: The following functions are aliases.
344  */
345 
346 int cucul_draw_circle(cucul_canvas_t *, int, int, int, uint32_t)
347          CACA_ALIAS(caca_draw_circle);
348 int cucul_draw_ellipse(cucul_canvas_t *, int, int, int, int, uint32_t)
349          CACA_ALIAS(caca_draw_ellipse);
350 int cucul_draw_thin_ellipse(cucul_canvas_t *, int, int, int, int)
351          CACA_ALIAS(caca_draw_thin_ellipse);
352 int cucul_fill_ellipse(cucul_canvas_t *, int, int, int, int, uint32_t)
353          CACA_ALIAS(caca_fill_ellipse);
354 
355