An LGPL/GPL-licensed artwork library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

art_vpath_dash.c 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /* Libart_LGPL - library of basic graphic primitives
  2. * Copyright (C) 1999-2000 Raph Levien
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  17. * Boston, MA 02111-1307, USA.
  18. */
  19. /* Apply a dash style to a vector path. */
  20. #include "config.h"
  21. #include "art_vpath_dash.h"
  22. #include <math.h>
  23. #include <stdlib.h>
  24. #include "art_misc.h"
  25. #include "art_vpath.h"
  26. /* Return the length of the largest subpath within vpath */
  27. static int
  28. art_vpath_dash_max_subpath (const ArtVpath *vpath)
  29. {
  30. int max_subpath;
  31. int i;
  32. int start;
  33. max_subpath = 0;
  34. start = 0;
  35. for (i = 0; vpath[i].code != ART_END; i++)
  36. {
  37. if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN)
  38. {
  39. if (i - start > max_subpath)
  40. max_subpath = i - start;
  41. start = i;
  42. }
  43. }
  44. if (i - start > max_subpath)
  45. max_subpath = i - start;
  46. return max_subpath;
  47. }
  48. /**
  49. * art_vpath_dash: Add dash style to vpath.
  50. * @vpath: Original vpath.
  51. * @dash: Dash style.
  52. *
  53. * Creates a new vpath that is the result of applying dash style @dash
  54. * to @vpath.
  55. *
  56. * This implementation has two known flaws:
  57. *
  58. * First, it adds a spurious break at the beginning of the vpath. The
  59. * only way I see to resolve this flaw is to run the state forward one
  60. * dash break at the beginning, and fix up by looping back to the
  61. * first dash break at the end. This is doable but of course adds some
  62. * complexity.
  63. *
  64. * Second, it does not suppress output points that are within epsilon
  65. * of each other.
  66. *
  67. * Return value: Newly created vpath.
  68. **/
  69. ArtVpath *
  70. art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash)
  71. {
  72. int max_subpath;
  73. double *dists;
  74. ArtVpath *result;
  75. int n_result, n_result_max;
  76. int start, end;
  77. int i;
  78. double total_dist;
  79. /* state while traversing dasharray - offset is offset of current dash
  80. value, toggle is 0 for "off" and 1 for "on", and phase is the distance
  81. in, >= 0, < dash->dash[offset]. */
  82. int offset, toggle;
  83. double phase;
  84. /* initial values */
  85. int offset_init, toggle_init;
  86. double phase_init;
  87. max_subpath = art_vpath_dash_max_subpath (vpath);
  88. dists = art_new (double, max_subpath);
  89. n_result = 0;
  90. n_result_max = 16;
  91. result = art_new (ArtVpath, n_result_max);
  92. /* determine initial values of dash state */
  93. toggle_init = 1;
  94. offset_init = 0;
  95. phase_init = dash->offset;
  96. while (phase_init >= dash->dash[offset_init])
  97. {
  98. toggle_init = !toggle_init;
  99. phase_init -= dash->dash[offset_init];
  100. offset_init++;
  101. if (offset_init == dash->n_dash)
  102. offset_init = 0;
  103. }
  104. for (start = 0; vpath[start].code != ART_END; start = end)
  105. {
  106. for (end = start + 1; vpath[end].code == ART_LINETO; end++);
  107. /* subpath is [start..end) */
  108. total_dist = 0;
  109. for (i = start; i < end - 1; i++)
  110. {
  111. double dx, dy;
  112. dx = vpath[i + 1].x - vpath[i].x;
  113. dy = vpath[i + 1].y - vpath[i].y;
  114. dists[i - start] = sqrt (dx * dx + dy * dy);
  115. total_dist += dists[i - start];
  116. }
  117. if (total_dist <= dash->dash[offset_init] - phase_init)
  118. {
  119. /* subpath fits entirely within first dash */
  120. if (toggle_init)
  121. {
  122. for (i = start; i < end; i++)
  123. art_vpath_add_point (&result, &n_result, &n_result_max,
  124. vpath[i].code, vpath[i].x, vpath[i].y);
  125. }
  126. }
  127. else
  128. {
  129. /* subpath is composed of at least one dash - thus all
  130. generated pieces are open */
  131. double dist;
  132. phase = phase_init;
  133. offset = offset_init;
  134. toggle = toggle_init;
  135. dist = 0;
  136. i = start;
  137. if (toggle)
  138. art_vpath_add_point (&result, &n_result, &n_result_max,
  139. ART_MOVETO_OPEN, vpath[i].x, vpath[i].y);
  140. while (i != end - 1)
  141. {
  142. if (dists[i - start] - dist > dash->dash[offset] - phase)
  143. {
  144. /* dash boundary is next */
  145. double a;
  146. double x, y;
  147. dist += dash->dash[offset] - phase;
  148. a = dist / dists[i - start];
  149. x = vpath[i].x + a * (vpath[i + 1].x - vpath[i].x);
  150. y = vpath[i].y + a * (vpath[i + 1].y - vpath[i].y);
  151. art_vpath_add_point (&result, &n_result, &n_result_max,
  152. toggle ? ART_LINETO : ART_MOVETO_OPEN,
  153. x, y);
  154. /* advance to next dash */
  155. toggle = !toggle;
  156. phase = 0;
  157. offset++;
  158. if (offset == dash->n_dash)
  159. offset = 0;
  160. }
  161. else
  162. {
  163. /* end of line in vpath is next */
  164. phase += dists[i - start] - dist;
  165. i++;
  166. dist = 0;
  167. if (toggle)
  168. art_vpath_add_point (&result, &n_result, &n_result_max,
  169. ART_LINETO, vpath[i].x, vpath[i].y);
  170. }
  171. }
  172. }
  173. }
  174. art_vpath_add_point (&result, &n_result, &n_result_max,
  175. ART_END, 0, 0);
  176. art_free (dists);
  177. return result;
  178. }