25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

407 lines
9.0 KiB

  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. /** \file line.c
  22. * \version \$Id$
  23. * \author Sam Hocevar <sam@zoy.org>
  24. * \brief Line drawing functions
  25. *
  26. * This file contains line and polyline drawing functions, with both thin
  27. * and thick styles.
  28. */
  29. #include "config.h"
  30. #if defined(HAVE_INTTYPES_H)
  31. # include <inttypes.h>
  32. #else
  33. typedef unsigned char uint8_t;
  34. #endif
  35. #include <stdlib.h>
  36. #include "caca.h"
  37. #include "caca_internals.h"
  38. struct line
  39. {
  40. int x1, y1;
  41. int x2, y2;
  42. char c;
  43. void (*draw) (struct line*);
  44. };
  45. static void clip_line(struct line*);
  46. static uint8_t clip_bits(int, int);
  47. static void draw_solid_line(struct line*);
  48. static void draw_thin_line(struct line*);
  49. /**
  50. * \brief Draw a line on the screen using the given character.
  51. *
  52. * \param x1 X coordinate of the first point.
  53. * \param y1 Y coordinate of the first point.
  54. * \param x2 X coordinate of the second point.
  55. * \param y2 Y coordinate of the second point.
  56. * \param c Character to draw the line with.
  57. * \return void
  58. */
  59. void caca_draw_line(int x1, int y1, int x2, int y2, char c)
  60. {
  61. struct line s;
  62. s.x1 = x1;
  63. s.y1 = y1;
  64. s.x2 = x2;
  65. s.y2 = y2;
  66. s.c = c;
  67. s.draw = draw_solid_line;
  68. clip_line(&s);
  69. }
  70. /**
  71. * \brief Draw a polyline on the screen using the given character and
  72. * coordinate arrays. The first and last points are not connected,
  73. * so in order to draw a polygon you need to specify the starting
  74. * point at the end of the list as well.
  75. *
  76. * \param x Array of X coordinates. Must have \p n + 1 elements.
  77. * \param y Array of Y coordinates. Must have \p n + 1 elements.
  78. * \param n Number of lines to draw.
  79. * \param c Character to draw the lines with.
  80. * \return void
  81. */
  82. void caca_draw_polyline(const int x[], const int y[], int n, char c)
  83. {
  84. int i;
  85. struct line s;
  86. s.c = c;
  87. s.draw = draw_solid_line;
  88. for(i = 0; i < n; i++)
  89. {
  90. s.x1 = x[i];
  91. s.y1 = y[i];
  92. s.x2 = x[i+1];
  93. s.y2 = y[i+1];
  94. clip_line(&s);
  95. }
  96. }
  97. /**
  98. * \brief Draw a thin line on the screen, using ASCII art.
  99. *
  100. * \param x1 X coordinate of the first point.
  101. * \param y1 Y coordinate of the first point.
  102. * \param x2 X coordinate of the second point.
  103. * \param y2 Y coordinate of the second point.
  104. * \return void
  105. */
  106. void caca_draw_thin_line(int x1, int y1, int x2, int y2)
  107. {
  108. struct line s;
  109. s.x1 = x1;
  110. s.y1 = y1;
  111. s.x2 = x2;
  112. s.y2 = y2;
  113. s.draw = draw_thin_line;
  114. clip_line(&s);
  115. }
  116. /**
  117. * \brief Draw a thin polyline on the screen using the given coordinate
  118. * arrays and with ASCII art. The first and last points are not
  119. * connected, so in order to draw a polygon you need to specify the
  120. * starting point at the end of the list as well.
  121. *
  122. * \param x Array of X coordinates. Must have \p n + 1 elements.
  123. * \param y Array of Y coordinates. Must have \p n + 1 elements.
  124. * \param n Number of lines to draw.
  125. * \return void
  126. */
  127. void caca_draw_thin_polyline(const int x[], const int y[], int n)
  128. {
  129. int i;
  130. struct line s;
  131. s.draw = draw_thin_line;
  132. for(i = 0; i < n; i++)
  133. {
  134. s.x1 = x[i];
  135. s.y1 = y[i];
  136. s.x2 = x[i+1];
  137. s.y2 = y[i+1];
  138. clip_line(&s);
  139. }
  140. }
  141. /*
  142. * XXX: The following functions are local.
  143. */
  144. /**
  145. * \brief Generic Cohen-Sutherland line clipping function.
  146. *
  147. * \param s a line structure
  148. * \return void
  149. */
  150. static void clip_line(struct line* s)
  151. {
  152. uint8_t bits1, bits2;
  153. bits1 = clip_bits(s->x1, s->y1);
  154. bits2 = clip_bits(s->x2, s->y2);
  155. if(bits1 & bits2)
  156. return;
  157. if(bits1 == 0)
  158. {
  159. if(bits2 == 0)
  160. s->draw(s);
  161. else
  162. {
  163. int tmp;
  164. tmp = s->x1; s->x1 = s->x2; s->x2 = tmp;
  165. tmp = s->y1; s->y1 = s->y2; s->y2 = tmp;
  166. clip_line(s);
  167. }
  168. return;
  169. }
  170. if(bits1 & (1<<0))
  171. {
  172. s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1);
  173. s->x1 = 0;
  174. }
  175. else if(bits1 & (1<<1))
  176. {
  177. int xmax = _caca_width - 1;
  178. s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1);
  179. s->x1 = xmax;
  180. }
  181. else if(bits1 & (1<<2))
  182. {
  183. s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1);
  184. s->y1 = 0;
  185. }
  186. else if(bits1 & (1<<3))
  187. {
  188. int ymax = _caca_height - 1;
  189. s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1);
  190. s->y1 = ymax;
  191. }
  192. clip_line(s);
  193. }
  194. /**
  195. * \brief Helper function for clip_line().
  196. *
  197. * \param x X coordinate of the point.
  198. * \param y Y coordinate of the point.
  199. * \return The clipping bits for the given point.
  200. */
  201. static uint8_t clip_bits(int x, int y)
  202. {
  203. uint8_t b = 0;
  204. if(x < 0)
  205. b |= (1<<0);
  206. else if(x >= (int)_caca_width)
  207. b |= (1<<1);
  208. if(y < 0)
  209. b |= (1<<2);
  210. else if(y >= (int)_caca_height)
  211. b |= (1<<3);
  212. return b;
  213. }
  214. /**
  215. * \brief Solid line drawing function, using Bresenham's mid-point line
  216. * scan-conversion algorithm.
  217. *
  218. * \param s a line structure
  219. * \return void
  220. */
  221. static void draw_solid_line(struct line* s)
  222. {
  223. int x1, y1, x2, y2;
  224. int dx, dy;
  225. int xinc, yinc;
  226. x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
  227. dx = abs(x2 - x1);
  228. dy = abs(y2 - y1);
  229. xinc = (x1 > x2) ? -1 : 1;
  230. yinc = (y1 > y2) ? -1 : 1;
  231. if(dx >= dy)
  232. {
  233. int dpr = dy << 1;
  234. int dpru = dpr - (dx << 1);
  235. int delta = dpr - dx;
  236. for(; dx>=0; dx--)
  237. {
  238. caca_putchar(x1, y1, s->c);
  239. if(delta > 0)
  240. {
  241. x1 += xinc;
  242. y1 += yinc;
  243. delta += dpru;
  244. }
  245. else
  246. {
  247. x1 += xinc;
  248. delta += dpr;
  249. }
  250. }
  251. }
  252. else
  253. {
  254. int dpr = dx << 1;
  255. int dpru = dpr - (dy << 1);
  256. int delta = dpr - dy;
  257. for(; dy >= 0; dy--)
  258. {
  259. caca_putchar(x1, y1, s->c);
  260. if(delta > 0)
  261. {
  262. x1 += xinc;
  263. y1 += yinc;
  264. delta += dpru;
  265. }
  266. else
  267. {
  268. y1 += yinc;
  269. delta += dpr;
  270. }
  271. }
  272. }
  273. }
  274. /**
  275. * \brief Thin line drawing function, using Bresenham's mid-point line
  276. * scan-conversion algorithm and ASCII art graphics.
  277. *
  278. * \param s a line structure
  279. * \return void
  280. */
  281. static void draw_thin_line(struct line* s)
  282. {
  283. char *charmapx, *charmapy;
  284. int x1, y1, x2, y2;
  285. int dx, dy;
  286. int yinc;
  287. if(s->x2 >= s->x1)
  288. {
  289. if(s->y1 > s->y2)
  290. charmapx = ",'";
  291. else
  292. charmapx = "`.";
  293. x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
  294. }
  295. else
  296. {
  297. if(s->y1 > s->y2)
  298. charmapx = "`.";
  299. else
  300. charmapx = ",'";
  301. x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2;
  302. }
  303. dx = abs(x2 - x1);
  304. dy = abs(y2 - y1);
  305. if(y1 > y2)
  306. {
  307. charmapy = ",'";
  308. yinc = -1;
  309. }
  310. else
  311. {
  312. yinc = 1;
  313. charmapy = "`.";
  314. }
  315. if(dx >= dy)
  316. {
  317. int dpr = dy << 1;
  318. int dpru = dpr - (dx << 1);
  319. int delta = dpr - dx;
  320. int prev = 0;
  321. for(; dx>=0; dx--)
  322. {
  323. if(delta > 0)
  324. {
  325. caca_putchar(x1, y1, charmapy[1]);
  326. x1++;
  327. y1 += yinc;
  328. delta += dpru;
  329. prev = 1;
  330. }
  331. else
  332. {
  333. if(prev)
  334. caca_putchar(x1, y1, charmapy[0]);
  335. else
  336. caca_putchar(x1, y1, '-');
  337. x1++;
  338. delta += dpr;
  339. prev = 0;
  340. }
  341. }
  342. }
  343. else
  344. {
  345. int dpr = dx << 1;
  346. int dpru = dpr - (dy << 1);
  347. int delta = dpr - dy;
  348. for(; dy >= 0; dy--)
  349. {
  350. if(delta > 0)
  351. {
  352. caca_putchar(x1, y1, charmapx[0]);
  353. caca_putchar(x1 + 1, y1, charmapx[1]);
  354. x1++;
  355. y1 += yinc;
  356. delta += dpru;
  357. }
  358. else
  359. {
  360. caca_putchar(x1, y1, '|');
  361. y1 += yinc;
  362. delta += dpr;
  363. }
  364. }
  365. }
  366. }