source: libcaca/trunk/src/line.c @ 298

Last change on this file since 298 was 298, checked in by Sam Hocevar, 17 years ago
  • doc/doxygen.cfg.in: + Removed caca_internal.h from the list of documented files.
  • src/: + Minor documentation updates.
  • Property svn:keywords set to Id
File size: 9.0 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002, 2003 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 *  02111-1307  USA
20 */
21
22/** \file line.c
23 *  \version \$Id: line.c 298 2003-12-31 14:21:08Z sam $
24 *  \author Sam Hocevar <sam@zoy.org>
25 *  \brief Line drawing
26 *
27 *  This file contains line and polyline drawing functions, with both thin
28 *  and thick styles.
29 */
30
31#include "config.h"
32
33#if defined(HAVE_INTTYPES_H)
34#   include <inttypes.h>
35#else
36typedef unsigned char uint8_t;
37#endif
38
39#include <stdlib.h>
40
41#include "caca.h"
42#include "caca_internals.h"
43
44struct line
45{
46    int x1, y1;
47    int x2, y2;
48    char c;
49    void (*draw) (struct line*);
50};
51
52static void clip_line(struct line*);
53static uint8_t clip_bits(int, int);
54static void draw_solid_line(struct line*);
55static void draw_thin_line(struct line*);
56
57/**
58 * \brief Draw a line on the screen using the given character.
59 *
60 * \param x1 X coordinate of the first point.
61 * \param y1 Y coordinate of the first point.
62 * \param x2 X coordinate of the second point.
63 * \param y2 Y coordinate of the second point.
64 * \param c Character to draw the line with.
65 * \return void
66 */
67void caca_draw_line(int x1, int y1, int x2, int y2, char c)
68{
69    struct line s;
70    s.x1 = x1;
71    s.y1 = y1;
72    s.x2 = x2;
73    s.y2 = y2;
74    s.c = c;
75    s.draw = draw_solid_line;
76    clip_line(&s);
77}
78
79/**
80 * \brief Draw a polyline on the screen using the given character and
81 *       coordinate arrays. The first and last points are not connected,
82 *       so in order to draw a polygon you need to specify the starting
83 *       point at the end of the list as well.
84 *
85 * \param x Array of X coordinates. Must have \p n + 1 elements.
86 * \param y Array of Y coordinates. Must have \p n + 1 elements.
87 * \param n Number of lines to draw.
88 * \param c Character to draw the lines with.
89 * \return void
90 */
91void caca_draw_polyline(const int x[], const int y[], int n, char c)
92{
93    int i;
94    struct line s;
95    s.c = c;
96    s.draw = draw_solid_line;
97
98    for(i = 0; i < n; i++)
99    {
100        s.x1 = x[i];
101        s.y1 = y[i];
102        s.x2 = x[i+1];
103        s.y2 = y[i+1];
104        clip_line(&s);
105    }
106}
107
108/**
109 * \brief Draw a thin line on the screen, using ASCII art.
110 *
111 * \param x1 X coordinate of the first point.
112 * \param y1 Y coordinate of the first point.
113 * \param x2 X coordinate of the second point.
114 * \param y2 Y coordinate of the second point.
115 * \return void
116 */
117void caca_draw_thin_line(int x1, int y1, int x2, int y2)
118{
119    struct line s;
120    s.x1 = x1;
121    s.y1 = y1;
122    s.x2 = x2;
123    s.y2 = y2;
124    s.draw = draw_thin_line;
125    clip_line(&s);
126}
127
128/**
129 * \brief Draw a thin polyline on the screen using the given coordinate
130 *       arrays and with ASCII art. The first and last points are not
131 *       connected, so in order to draw a polygon you need to specify the
132 *       starting point at the end of the list as well.
133 *
134 * \param x Array of X coordinates. Must have \p n + 1 elements.
135 * \param y Array of Y coordinates. Must have \p n + 1 elements.
136 * \param n Number of lines to draw.
137 * \return void
138 */
139void caca_draw_thin_polyline(const int x[], const int y[], int n)
140{
141    int i;
142    struct line s;
143    s.draw = draw_thin_line;
144
145    for(i = 0; i < n; i++)
146    {
147        s.x1 = x[i];
148        s.y1 = y[i];
149        s.x2 = x[i+1];
150        s.y2 = y[i+1];
151        clip_line(&s);
152    }
153}
154
155/*
156 * XXX: The following functions are local.
157 */
158
159/**
160 * \brief Generic Cohen-Sutherland line clipping function.
161 *
162 * \param s a line structure
163 * \return void
164 */
165static void clip_line(struct line* s)
166{
167    uint8_t bits1, bits2;
168
169    bits1 = clip_bits(s->x1, s->y1);
170    bits2 = clip_bits(s->x2, s->y2);
171
172    if(bits1 & bits2)
173        return;
174
175    if(bits1 == 0)
176    {
177        if(bits2 == 0)
178            s->draw(s);
179        else
180        {
181            int tmp;
182            tmp = s->x1; s->x1 = s->x2; s->x2 = tmp;
183            tmp = s->y1; s->y1 = s->y2; s->y2 = tmp;
184            clip_line(s);
185        }
186
187        return;
188    }
189
190    if(bits1 & (1<<0))
191    {
192        s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1);
193        s->x1 = 0;
194    }
195    else if(bits1 & (1<<1))
196    {
197        int xmax = _caca_width - 1;
198        s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1);
199        s->x1 = xmax;
200    }
201    else if(bits1 & (1<<2))
202    {
203        s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1);
204        s->y1 = 0;
205    }
206    else if(bits1 & (1<<3))
207    {
208        int ymax = _caca_height - 1;
209        s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1);
210        s->y1 = ymax;
211    }
212
213    clip_line(s);
214}
215
216/**
217 * \brief Helper function for clip_line().
218 *
219 * \param x X coordinate of the point.
220 * \param y Y coordinate of the point.
221 * \return The clipping bits for the given point.
222 */
223static uint8_t clip_bits(int x, int y)
224{
225    uint8_t b = 0;
226
227    if(x < 0)
228        b |= (1<<0);
229    else if(x >= (int)_caca_width)
230        b |= (1<<1);
231
232    if(y < 0)
233        b |= (1<<2);
234    else if(y >= (int)_caca_height)
235        b |= (1<<3);
236
237    return b;
238}
239
240/**
241 * \brief Solid line drawing function, using Bresenham's mid-point line
242 *       scan-conversion algorithm.
243 *
244 * \param s a line structure
245 * \return void
246 */
247static void draw_solid_line(struct line* s)
248{
249    int x1, y1, x2, y2;
250    int dx, dy;
251    int xinc, yinc;
252
253    x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
254
255    dx = abs(x2 - x1);
256    dy = abs(y2 - y1);
257
258    xinc = (x1 > x2) ? -1 : 1;
259    yinc = (y1 > y2) ? -1 : 1;
260
261    if(dx >= dy)
262    {
263        int dpr = dy << 1;
264        int dpru = dpr - (dx << 1);
265        int delta = dpr - dx;
266
267        for(; dx>=0; dx--)
268        {
269            caca_putchar(x1, y1, s->c);
270            if(delta > 0)
271            {
272                x1 += xinc;
273                y1 += yinc;
274                delta += dpru;
275            }
276            else
277            {
278                x1 += xinc;
279                delta += dpr;
280            }
281        }
282    }
283    else
284    {
285        int dpr = dx << 1;
286        int dpru = dpr - (dy << 1);
287        int delta = dpr - dy;
288
289        for(; dy >= 0; dy--)
290        {
291            caca_putchar(x1, y1, s->c);
292            if(delta > 0)
293            {
294                x1 += xinc;
295                y1 += yinc;
296                delta += dpru;
297            }
298            else
299            {
300                y1 += yinc;
301                delta += dpr;
302            }
303        }
304    }
305}
306
307/**
308 * \brief Thin line drawing function, using Bresenham's mid-point line
309 *        scan-conversion algorithm and ASCII art graphics.
310 *
311 * \param s a line structure
312 * \return void
313 */
314static void draw_thin_line(struct line* s)
315{
316    char *charmapx, *charmapy;
317    int x1, y1, x2, y2;
318    int dx, dy;
319    int yinc;
320
321    if(s->x2 >= s->x1)
322    {
323        if(s->y1 > s->y2)
324            charmapx = ",'";
325        else
326            charmapx = "`.";
327        x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
328    }
329    else
330    {
331        if(s->y1 > s->y2)
332            charmapx = "`.";
333        else
334            charmapx = ",'";
335        x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2;
336    }
337
338    dx = abs(x2 - x1);
339    dy = abs(y2 - y1);
340
341    if(y1 > y2)
342    {
343        charmapy = ",'";
344        yinc = -1;
345    }
346    else
347    {
348        yinc = 1;
349        charmapy = "`.";
350    }
351
352    if(dx >= dy)
353    {
354        int dpr = dy << 1;
355        int dpru = dpr - (dx << 1);
356        int delta = dpr - dx;
357        int prev = 0;
358
359        for(; dx>=0; dx--)
360        {
361            if(delta > 0)
362            {
363                caca_putchar(x1, y1, charmapy[1]);
364                x1++;
365                y1 += yinc;
366                delta += dpru;
367                prev = 1;
368            }
369            else
370            {
371                if(prev)
372                    caca_putchar(x1, y1, charmapy[0]);
373                else
374                    caca_putchar(x1, y1, '-');
375                x1++;
376                delta += dpr;
377                prev = 0;
378            }
379        }
380    }
381    else
382    {
383        int dpr = dx << 1;
384        int dpru = dpr - (dy << 1);
385        int delta = dpr - dy;
386
387        for(; dy >= 0; dy--)
388        {
389            if(delta > 0)
390            {
391                caca_putchar(x1, y1, charmapx[0]);
392                caca_putchar(x1 + 1, y1, charmapx[1]);
393                x1++;
394                y1 += yinc;
395                delta += dpru;
396            }
397            else
398            {
399                caca_putchar(x1, y1, '|');
400                y1 += yinc;
401                delta += dpr;
402            }
403        }
404    }
405}
406
Note: See TracBrowser for help on using the repository browser.