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
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This file contains the line method, which is a standard part of libplot.
20 It draws an object: a line segment extending from the point x0,y0 to the
21 point x1,y1. By repeatedly invoking cont(), the user may extend this
22 line object to a polyline object. As implemented, the line method is a
23 wrapper around the cont method. */
24
25 /* This file also contains the cont method, which is a standard part of
26 libplot. It continues a line from the current position of the graphics
27 cursor to the point specified by x and y.
28
29 This method is used in the construction of paths. By repeatedly
30 invoking cont(), the user may construct a polyline of arbitrary length.
31 arc() and ellarc() may also be invoked, to add circular or elliptic arc
32 elements to the path. The path will terminate when the user either
33
34 (1) explicitly invokes the endpath() method, or
35 (2) changes the value of one of the relevant drawing attributes,
36 e.g. by invoking move(), linemod(), linewidth(), pencolor(),
37 fillcolor(), or filltype(), or
38 (3) draws some non-path object, by invoking box(),
39 circle(), point(), label(), alabel(), etc., or
40 (4) invokes restorestate() to restore an earlier drawing state. */
41
42 #include "sys-defines.h"
43 #include "extern.h"
44
45 int
_API_fline(R___ (Plotter * _plotter)double x0,double y0,double x1,double y1)46 _API_fline (R___(Plotter *_plotter) double x0, double y0, double x1, double y1)
47 {
48 if (!_plotter->data->open)
49 {
50 _plotter->error (R___(_plotter)
51 "fline: invalid operation");
52 return -1;
53 }
54
55 if (_plotter->drawstate->path != (plPath *)NULL
56 && (_plotter->drawstate->path->type != PATH_SEGMENT_LIST
57 ||
58 (_plotter->drawstate->path->type == PATH_SEGMENT_LIST
59 && _plotter->drawstate->path->primitive)))
60 /* There's a simple path under construction (so that endsubpath() must
61 not have been invoked), and it contains a closed primitive
62 (box/circle/ellipse). So flush out the whole compound path. (It
63 may include other, previously drawn simple paths.) */
64 _API_endpath (S___(_plotter));
65
66 /* if new segment not contiguous, move to its starting point (first
67 flushing out the compound path under construction, if any) */
68 if (x0 != _plotter->drawstate->pos.x
69 || y0 != _plotter->drawstate->pos.y)
70 {
71 if (_plotter->drawstate->path)
72 _API_endpath (S___(_plotter));
73 _plotter->drawstate->pos.x = x0;
74 _plotter->drawstate->pos.y = y0;
75 }
76
77 return _API_fcont (R___(_plotter) x1, y1);
78 }
79
80 int
_API_fcont(R___ (Plotter * _plotter)double x,double y)81 _API_fcont (R___(Plotter *_plotter) double x, double y)
82 {
83 int prev_num_segments;
84 plPoint p0, p1;
85
86 if (!_plotter->data->open)
87 {
88 _plotter->error (R___(_plotter)
89 "fcont: invalid operation");
90 return -1;
91 }
92
93 if (_plotter->drawstate->path != (plPath *)NULL
94 && (_plotter->drawstate->path->type != PATH_SEGMENT_LIST
95 ||
96 (_plotter->drawstate->path->type == PATH_SEGMENT_LIST
97 && _plotter->drawstate->path->primitive)))
98 /* There's a simple path under construction (so that endsubpath() must
99 not have been invoked), and it contains a closed primitive
100 (box/circle/ellipse). So flush out the whole compound path. (It
101 may include other, previously drawn simple paths.) */
102 _API_endpath (S___(_plotter));
103
104 p0 = _plotter->drawstate->pos;
105 p1.x = x; p1.y = y;
106
107 if (_plotter->drawstate->path == (plPath *)NULL)
108 /* begin a new path, of segment list type */
109 {
110 _plotter->drawstate->path = _new_plPath ();
111 prev_num_segments = 0;
112 _add_moveto (_plotter->drawstate->path, p0);
113 }
114 else
115 prev_num_segments = _plotter->drawstate->path->num_segments;
116
117 /* if segment buffer is occupied by a single arc, replace arc by a
118 polyline if that's called for (Plotter-dependent) */
119 if (_plotter->data->have_mixed_paths == false
120 && _plotter->drawstate->path->num_segments == 2)
121 {
122 _pl_g_maybe_replace_arc (S___(_plotter));
123 if (_plotter->drawstate->path->num_segments > 2)
124 prev_num_segments = 0;
125 }
126
127 /* add new line segment to the path buffer */
128 _add_line (_plotter->drawstate->path, p1);
129
130 /* move to endpoint */
131 _plotter->drawstate->pos = p1;
132
133 /* pass all the newly added segments to the Plotter-specific function
134 maybe_paint_segments(), since some Plotters plot paths in real time,
135 i.e., prepaint them, rather than waiting until endpath() is called */
136 _plotter->maybe_prepaint_segments (R___(_plotter) prev_num_segments);
137
138 /* If the path is getting too long (and it doesn't have to be filled),
139 flush it out by invoking endpath(), and begin a new one. `Too long'
140 is Plotter-dependent; some don't do this flushing at all. */
141 if ((_plotter->drawstate->path->num_segments
142 >= _plotter->data->max_unfilled_path_length)
143 && (_plotter->drawstate->fill_type == 0)
144 && _plotter->path_is_flushable (S___(_plotter)))
145 _API_endpath (S___(_plotter));
146
147 return 0;
148 }
149
150 /* Some Plotters, such as FigPlotters, support the drawing of single arc
151 segments as primitives, but they don't allow mixed segment lists to
152 appear in the path storage buffer, because they don't know how to handle
153 them. A `mixed segment list' is a sequence of arc segments interspersed
154 with line segments, or a path consisting of more than a single
155 contiguous arc. I.e., a mixed path is defined by a segment list that is
156 not a pure polyline and has length >1.
157
158 For such Plotters, this function may be invoked by fcont() or farc() or
159 fellarc() or fbezier2() or fbezier3(), to delete a single arc from the
160 buffer and replace it by its polygonal approximation, i.e. by a
161 polyline. I.e., it is invoked to keep the stored path from becoming
162 mixed. */
163
164 void
_pl_g_maybe_replace_arc(S___ (Plotter * _plotter))165 _pl_g_maybe_replace_arc (S___(Plotter *_plotter))
166 {
167 /* sanity check */
168 if (!(_plotter->data->have_mixed_paths == false
169 && _plotter->drawstate->path->num_segments == 2))
170 return;
171
172 switch (_plotter->drawstate->path->segments[1].type)
173 {
174 plPoint pc, pd, p1;
175
176 case S_ARC:
177 /* segment buffer contains a single circular arc segment, so remove it */
178 pc = _plotter->drawstate->path->segments[1].pc;
179 p1 = _plotter->drawstate->path->segments[1].p;
180 _plotter->drawstate->path->num_segments = 1;
181
182 /* add polygonal approximation to circular arc to the segment buffer */
183 _add_arc_as_lines (_plotter->drawstate->path, pc, p1);
184 break;
185
186 case S_ELLARC:
187 /* segment buffer contains a single elliptic arc segment, so remove it */
188 pc = _plotter->drawstate->path->segments[1].pc;
189 p1 = _plotter->drawstate->path->segments[1].p;
190 _plotter->drawstate->path->num_segments = 1;
191
192 /* add polygonal approximation to elliptic arc to the segment buffer */
193 _add_ellarc_as_lines (_plotter->drawstate->path, pc, p1);
194 break;
195
196 case S_QUAD:
197 /* segment buffer contains a single quad. Bezier segment, so remove it */
198 pc = _plotter->drawstate->path->segments[1].pc;
199 p1 = _plotter->drawstate->path->segments[1].p;
200 _plotter->drawstate->path->num_segments = 1;
201
202 /* add polygonal approximation to quad. Bezier to the segment buffer */
203 _add_bezier2_as_lines (_plotter->drawstate->path, pc, p1);
204 break;
205
206 case S_CUBIC:
207 /* segment buffer contains a single cubic Bezier segment, so remove it */
208 pc = _plotter->drawstate->path->segments[1].pc;
209 pd = _plotter->drawstate->path->segments[1].pd;
210 p1 = _plotter->drawstate->path->segments[1].p;
211 _plotter->drawstate->path->num_segments = 1;
212
213 /* add polygonal approximation to cubic Bezier to the segment buffer */
214 _add_bezier3_as_lines (_plotter->drawstate->path, pc, pd, p1);
215 break;
216
217 default:
218 /* other segment type (presumably a line segment, see above); OK */
219 break;
220 }
221 }
222