Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

399 righe
8.8 KiB

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