g_arc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /* This file is part of the GNU plotutils package. Copyright (C) 1995,
  2. 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
  3. The GNU plotutils package is free software. You may redistribute it
  4. and/or modify it under the terms of the GNU General Public License as
  5. published by the Free Software foundation; either version 2, or (at your
  6. option) any later version.
  7. The GNU plotutils package is distributed in the hope that it will be
  8. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. General Public License for more details.
  11. You should have received a copy of the GNU General Public License along
  12. with the GNU plotutils package; see the file COPYING. If not, write to
  13. the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
  14. Boston, MA 02110-1301, USA. */
  15. /* This file contains the arc method, which is a standard part of libplot.
  16. It draws an object: a circular arc from x0,y0 to x1,y1, with center at
  17. xc,yc. If xc,yc does not lie on the perpendicular bisector between the
  18. other two points as it should, it is adjusted so that it does.
  19. This file also contains the ellarc method, which is a GNU extension to
  20. libplot. It draws an object: an arc of an ellipse, from p0=(x0,y0) to
  21. p1=(x1,y1). The center of the ellipse will be at pc=(xc,yc).
  22. These conditions do not uniquely determine the elliptic arc (or the
  23. ellipse of which it is an arc). We choose the elliptic arc so that it
  24. has control points p0, p1, and p0 + p1 - pc, where the third control
  25. point p0 + p1 - pc is simply the reflection of pc through the line
  26. determined by p0 and p1. This means that the arc passes through p0 and
  27. p1, is tangent at p0 to the line segment joining p0 to p0 + p1 - pc, and
  28. is tangent at p1 to the line segment joining p1 to p0 + p1 - pc. So it
  29. fits snugly into a triangle, the vertices of which are the three control
  30. points.
  31. This sort of elliptic arc is called a `quarter-ellipse', since it is an
  32. affinely transformed quarter-circle. Specifically, it is an affinely
  33. transformed version of the first quadrant of a unit circle, with the
  34. affine transformation mapping (0,0) to pc, (0,1) to p0, (1,0) to p1, and
  35. (1,1) to the control point p0 + p1 - pc. */
  36. #include "sys-defines.h"
  37. #include "extern.h"
  38. #define COLLINEAR(p0, p1, p2) \
  39. ((p0.x * p1.y - p0.y * p1.x - p0.x * p2.y + \
  40. p0.y * p2.x + p1.x * p2.y - p1.y * p2.x) == 0.0)
  41. int
  42. _API_farc (R___(Plotter *_plotter) double xc, double yc, double x0, double y0, double x1, double y1)
  43. {
  44. int prev_num_segments;
  45. plPoint p0, p1, pc;
  46. if (!_plotter->data->open)
  47. {
  48. _plotter->error (R___(_plotter)
  49. "farc: invalid operation");
  50. return -1;
  51. }
  52. if (_plotter->drawstate->path != (plPath *)NULL
  53. && (_plotter->drawstate->path->type != PATH_SEGMENT_LIST
  54. ||
  55. (_plotter->drawstate->path->type == PATH_SEGMENT_LIST
  56. && _plotter->drawstate->path->primitive)))
  57. /* There's a simple path under construction (so that endsubpath() must
  58. not have been invoked), and it contains a closed primitive
  59. (box/circle/ellipse). So flush out the whole compound path. (It
  60. may include other, previously drawn simple paths.) */
  61. _API_endpath (S___(_plotter));
  62. /* if new segment not contiguous, move to its starting point (first
  63. flushing out the compound path under construction, if any) */
  64. if (x0 != _plotter->drawstate->pos.x
  65. || y0 != _plotter->drawstate->pos.y)
  66. {
  67. if (_plotter->drawstate->path)
  68. _API_endpath (S___(_plotter));
  69. _plotter->drawstate->pos.x = x0;
  70. _plotter->drawstate->pos.y = y0;
  71. }
  72. p0.x = x0; p0.y = y0;
  73. p1.x = x1; p1.y = y1;
  74. pc.x = xc; pc.y = yc;
  75. if (_plotter->drawstate->path == (plPath *)NULL)
  76. /* begin a new path, of segment list type */
  77. {
  78. _plotter->drawstate->path = _new_plPath ();
  79. prev_num_segments = 0;
  80. _add_moveto (_plotter->drawstate->path, p0);
  81. }
  82. else
  83. prev_num_segments = _plotter->drawstate->path->num_segments;
  84. /* Trivial case: if linemode is "disconnected", just plot a line segment
  85. from (x0,y0) to (x1,y1). Only the endpoints will appear on the
  86. display. */
  87. if (!_plotter->drawstate->points_are_connected)
  88. _add_line (_plotter->drawstate->path, p1);
  89. /* Another trivial case: treat a zero-length arc as a line segment */
  90. else if (x0 == x1 && y0 == y1)
  91. _add_line (_plotter->drawstate->path, p1);
  92. else
  93. /* standard (non-trivial) case */
  94. {
  95. /* if segment buffer is occupied by a single arc, replace arc by a
  96. polyline if that's called for (Plotter-dependent) */
  97. if (_plotter->data->have_mixed_paths == false
  98. && _plotter->drawstate->path->num_segments == 2)
  99. {
  100. _pl_g_maybe_replace_arc (S___(_plotter));
  101. if (_plotter->drawstate->path->num_segments > 2)
  102. prev_num_segments = 0;
  103. }
  104. /* add new circular arc to the path buffer */
  105. /* adjust location of pc if necessary, to place it on the bisector */
  106. pc = _truecenter (p0, p1, pc);
  107. /* add new circular arc (either real or fake) to path buffer */
  108. if (((_plotter->data->have_mixed_paths == false
  109. && _plotter->drawstate->path->num_segments == 1) /* i.e. moveto */
  110. || _plotter->data->have_mixed_paths == true)
  111. && (_plotter->data->allowed_arc_scaling == AS_ANY
  112. || (_plotter->data->allowed_arc_scaling == AS_UNIFORM
  113. && _plotter->drawstate->transform.uniform)
  114. || (_plotter->data->allowed_arc_scaling == AS_AXES_PRESERVED
  115. && _plotter->drawstate->transform.axes_preserved)))
  116. /* add circular arc as an arc element, since it's allowed */
  117. _add_arc (_plotter->drawstate->path, pc, p1);
  118. else if (_plotter->data->allowed_cubic_scaling == AS_ANY)
  119. /* add a cubic Bezier that approximates the circular arc (allowed
  120. since this Plotter supports cubic Beziers) */
  121. _add_arc_as_bezier3 (_plotter->drawstate->path, pc, p1);
  122. else
  123. /* add a polygonal approximation */
  124. _add_arc_as_lines (_plotter->drawstate->path, pc, p1);
  125. }
  126. /* move to endpoint */
  127. _plotter->drawstate->pos = p1;
  128. /* pass all the newly added segments to the Plotter-specific function
  129. maybe_paint_segments(), since some Plotters plot paths in real time,
  130. i.e., prepaint them, rather than waiting until endpath() is called */
  131. _plotter->maybe_prepaint_segments (R___(_plotter) prev_num_segments);
  132. /* If the path is getting too long (and it doesn't have to be filled),
  133. flush it out by invoking endpath(), and begin a new one. `Too long'
  134. is Plotter-dependent; some don't do this flushing at all. */
  135. if ((_plotter->drawstate->path->num_segments
  136. >= _plotter->data->max_unfilled_path_length)
  137. && (_plotter->drawstate->fill_type == 0)
  138. && _plotter->path_is_flushable (S___(_plotter)))
  139. _API_endpath (S___(_plotter));
  140. return 0;
  141. }
  142. int
  143. _API_fellarc (R___(Plotter *_plotter) double xc, double yc, double x0, double y0, double x1, double y1)
  144. {
  145. int prev_num_segments;
  146. plPoint pc, p0, p1;
  147. if (!_plotter->data->open)
  148. {
  149. _plotter->error (R___(_plotter)
  150. "fellarc: invalid operation");
  151. return -1;
  152. }
  153. if (_plotter->drawstate->path != (plPath *)NULL
  154. && (_plotter->drawstate->path->type != PATH_SEGMENT_LIST
  155. ||
  156. (_plotter->drawstate->path->type == PATH_SEGMENT_LIST
  157. && _plotter->drawstate->path->primitive)))
  158. /* There's a simple path under construction (so that endsubpath() must
  159. not have been invoked), and it contains a closed primitive
  160. (box/circle/ellipse). So flush out the whole compound path. (It
  161. may include other, previously drawn simple paths.) */
  162. _API_endpath (S___(_plotter));
  163. /* if new segment not contiguous, move to its starting point (first
  164. flushing out the compound path under construction, if any) */
  165. if (x0 != _plotter->drawstate->pos.x
  166. || y0 != _plotter->drawstate->pos.y)
  167. {
  168. if (_plotter->drawstate->path)
  169. _API_endpath (S___(_plotter));
  170. _plotter->drawstate->pos.x = x0;
  171. _plotter->drawstate->pos.y = y0;
  172. }
  173. p0.x = x0; p0.y = y0;
  174. p1.x = x1; p1.y = y1;
  175. pc.x = xc; pc.y = yc;
  176. if (_plotter->drawstate->path == (plPath *)NULL)
  177. /* begin a new path, of segment list type */
  178. {
  179. _plotter->drawstate->path = _new_plPath ();
  180. prev_num_segments = 0;
  181. _add_moveto (_plotter->drawstate->path, p0);
  182. }
  183. else
  184. prev_num_segments = _plotter->drawstate->path->num_segments;
  185. if (!_plotter->drawstate->points_are_connected)
  186. /* Trivial case: if linemode is "disconnected", just plot a line
  187. segment from (x0,y0) to (x1,y1). Only the endpoints will appear on
  188. the display. */
  189. _add_line (_plotter->drawstate->path, p1);
  190. else if (x0 == x1 && y0 == y1)
  191. /* Another trivial case: treat a zero-length arc as a line segment */
  192. _add_line (_plotter->drawstate->path, p1);
  193. else if (COLLINEAR (p0, p1, pc))
  194. /* yet another trivial case, collinear points: draw line segment
  195. from p0 to p1 */
  196. _add_line (_plotter->drawstate->path, p1);
  197. else
  198. /* standard (nontrivial) case */
  199. {
  200. /* if segment buffer is occupied by a single arc, replace arc by a
  201. polyline if that's called for (Plotter-dependent) */
  202. if (_plotter->data->have_mixed_paths == false
  203. && _plotter->drawstate->path->num_segments == 2)
  204. {
  205. _pl_g_maybe_replace_arc (S___(_plotter));
  206. if (_plotter->drawstate->path->num_segments > 2)
  207. prev_num_segments = 0;
  208. }
  209. /* add new elliptic arc (either real or fake) to path buffer */
  210. if (((_plotter->data->have_mixed_paths == false
  211. && _plotter->drawstate->path->num_segments == 1) /* i.e. moveto */
  212. || _plotter->data->have_mixed_paths == true)
  213. && (_plotter->data->allowed_ellarc_scaling == AS_ANY
  214. || (_plotter->data->allowed_ellarc_scaling == AS_UNIFORM
  215. && _plotter->drawstate->transform.uniform)
  216. || (_plotter->data->allowed_ellarc_scaling == AS_AXES_PRESERVED
  217. && _plotter->drawstate->transform.axes_preserved
  218. && ((y0 == yc && x1 == xc) || (x0 == xc && y1 == yc)))))
  219. /* add elliptic arc to the path buffer as an arc element, since
  220. it's allowed (note that we interpret the AS_AXES_PRESERVED
  221. constraint to require also that the x and y coors for arc
  222. endpoints line up) */
  223. _add_ellarc (_plotter->drawstate->path, pc, p1);
  224. else if (_plotter->data->allowed_cubic_scaling == AS_ANY)
  225. /* add a cubic Bezier that approximates the elliptic arc (allowed
  226. since this Plotter supports cubic Beziers) */
  227. _add_ellarc_as_bezier3 (_plotter->drawstate->path, pc, p1);
  228. else
  229. /* add a polygonal approximation to the elliptic arc */
  230. _add_ellarc_as_lines (_plotter->drawstate->path, pc, p1);
  231. }
  232. /* move to endpoint */
  233. _plotter->drawstate->pos = p1;
  234. /* pass all the newly added segments to the Plotter-specific function
  235. maybe_paint_segments(), since some Plotters plot paths in real time,
  236. i.e., prepaint them, rather than waiting until endpath() is called */
  237. _plotter->maybe_prepaint_segments (R___(_plotter) prev_num_segments);
  238. /* If the path is getting too long (and it doesn't have to be filled),
  239. flush it out by invoking endpath(), and begin a new one. `Too long'
  240. is Plotter-dependent; some don't do this flushing at all. */
  241. if ((_plotter->drawstate->path->num_segments
  242. >= _plotter->data->max_unfilled_path_length)
  243. && (_plotter->drawstate->fill_type == 0)
  244. && _plotter->path_is_flushable (S___(_plotter)))
  245. _API_endpath (S___(_plotter));
  246. return 0;
  247. }