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 endpath() method, which is a GNU extension to
20    libplot.  A path object may be constructed incrementally, by repeated
21    invocation of such operations as cont(), arc(), etc.  The construction
22    may be terminated, and the path object finalized, by an explict
23    invocation of endpath().  If endpath() is invoked when no path is under
24    construction, it has no effect. */
25 
26 /* endpath() is a wrapper around the internal paint_path() method, which
27    any Plotter can define as it chooses.  Any path is stored as a plPath
28    structure in the drawing state.  This may contain a list of segments
29    (line segments, curve segments etc.; which are allowed is
30    Plotter-dependent) or simply a Plotter-specific drawing primitive such
31    as a circle, ellipse, or rectangle.  paint_path() should be able to
32    handle anything that is appropriate for the given type of Plotter. */
33 
34 /* This file also contains the endsubpath() and closepath() methods. */
35 
36 #include "sys-defines.h"
37 #include "extern.h"
38 
39 int
_API_endpath(S___ (Plotter * _plotter))40 _API_endpath (S___(Plotter *_plotter))
41 {
42   int i;
43 
44   if (!_plotter->data->open)
45     {
46       _plotter->error (R___(_plotter)
47 		       "endpath: invalid operation");
48       return -1;
49     }
50 
51   /* end simple path under construction (if any), and move it to the array
52      of stored simple paths */
53   _API_endsubpath (S___(_plotter));
54 
55   if (_plotter->drawstate->num_paths == 0)
56     /* no stored simple paths; so nothing to do, and we're out of here */
57     return 0;
58 
59   /* at this point, compound path is available as an array of simple paths,
60      of length at least 1, in _plotter->drawstate->paths[] */
61 
62   /* Two cases: either the line mode is `disconnected', or it isn't (the
63      normal case). */
64 
65   if (!_plotter->drawstate->points_are_connected)
66     /* Special case: "disconnected" linemode.  If we have a pen, path will
67        be drawn as a sequence of filled circles, one per juncture point.
68        Path will not be filled (our convention). */
69     {
70       if (_plotter->drawstate->pen_type != 0)
71 	/* have a pen, so we can draw something */
72 	{
73 	  plPath **saved_paths;
74 	  int saved_num_paths;
75 	  double radius = 0.5 * _plotter->drawstate->line_width;
76 	  int i;
77 
78 	  /* Switch to a temporary paths buffer.  Needed because the
79 	     fcircle() method calls endpath(), which would otherwise mess
80 	     up the real paths buffer. */
81 	  saved_paths = _plotter->drawstate->paths;
82 	  saved_num_paths = _plotter->drawstate->num_paths;
83 	  _plotter->drawstate->paths = (plPath **)NULL;
84 	  _plotter->drawstate->num_paths = 0;
85 
86 	  /* save graphics state */
87 	  _API_savestate (S___(_plotter));
88 
89 	  /* set attributes appropriate for drawing filled edgeless
90 	     circles, in the current pen (rather than filling) color */
91 	  _API_filltype (R___(_plotter) 1);
92 	  _API_fillcolor (R___(_plotter)
93 			       _plotter->drawstate->fgcolor.red,
94 			       _plotter->drawstate->fgcolor.green,
95 			       _plotter->drawstate->fgcolor.blue);
96 	  _API_pentype (R___(_plotter) 0); /* edgeless */
97 	  _API_linemod (R___(_plotter) "solid"); /* necessary; see below*/
98 
99 	  /* loop over saved simple paths */
100 	  for (i = 0; i < saved_num_paths; i++)
101 	    {
102 	      plPath *path;
103 	      bool closed;
104 	      int j;
105 
106 	      path = saved_paths[i];
107 
108 	      /* sanity check: if linemode is disconnected, we should never
109 		 have created any simple path other than a segment list;
110 		 also, should have at least two juncture points */
111 	      if (path->type != PATH_SEGMENT_LIST || path->num_segments < 2)
112 		continue;
113 
114 	      /* check for closure */
115 	      if ((path->num_segments >= 3)
116 		  && (path->segments[path->num_segments - 1].p.x ==
117 		      path->segments[0].p.x)
118 		  && (path->segments[path->num_segments - 1].p.y ==
119 		      path->segments[0].p.y))
120 		closed = true;
121 	      else
122 		closed = false;		/* 2-point ones should be open */
123 
124 	      /* draw each point as a filled circle, diameter = line width */
125 	      for (j = 0; j < path->num_segments - (closed ? 1 : 0); j++)
126 		_API_fcircle (R___(_plotter)
127 			      path->segments[j].p.x,
128 			      path->segments[j].p.y,
129 			      radius);
130 	      if (closed)
131 		/* restore graphics cursor */
132 		_plotter->drawstate->pos = path->segments[0].p;
133 	    }
134 
135 	  /* Restore graphics state.  This will first do a recursive
136 	     endpath() and hence reset the newly populated paths buffer.
137 	     That won't result in infinite recursion: since the line type
138 	     was set to "solid" above, the `points_are_connected' element
139 	     is now `false', and this code won't be invoked again. */
140 	  _API_restorestate (S___(_plotter));
141 
142 	  /* switch back to original paths buffer */
143 	  _plotter->drawstate->paths = saved_paths;
144 	  _plotter->drawstate->num_paths = saved_num_paths;
145 	}
146     }
147 
148   else
149     /* normal case: line mode isn't disconnected, so no contortions needed */
150     {
151       if (_plotter->drawstate->num_paths == 1)
152 	/* compound path is just a single simple path, so paint it by
153 	   calling the Plotter-specific paint_path() method (the painting
154 	   may involve both filling and/or edging) */
155 	{
156 	  _plotter->drawstate->path = _plotter->drawstate->paths[0];
157 	  _plotter->paint_path (S___(_plotter));
158 	  _plotter->drawstate->path = (plPath *)NULL;
159 	}
160       else
161 	/* compound path comprises more than one simple path */
162 	{
163 	  /* first, attempt to use Plotter-specific support for painting
164 	     compound paths (not many Plotters have this) */
165 
166 	  if (_plotter->paint_paths (S___(_plotter)) == false)
167 	    /* Plotter either has no such support, or was unable to paint
168 	       this particular compound path; so we paint it in a clever,
169 	       device-independent way.  For filling, we merge the simple
170 	       paths into a single path, and invoke paint_path() on the
171 	       result.  For edging, we stroke each of the simple paths
172 	       individually. */
173 	    {
174 	      int fill_type, pen_type;
175 
176 	      fill_type = _plotter->drawstate->fill_type;
177 	      pen_type = _plotter->drawstate->pen_type;
178 
179 	      if (fill_type && _plotter->data->have_solid_fill)
180 		/* fill the compound path, by merging its simple paths into
181 		   a single simple path, and then invoking paint_path() on
182 		   the result */
183 		{
184 		  plPath **merged_paths;
185 		  _plotter->drawstate->fill_type = fill_type;
186 		  _plotter->drawstate->pen_type = 0; /* unedged */
187 
188 		  merged_paths = _merge_paths ((const plPath **)_plotter->drawstate->paths,
189 					       _plotter->drawstate->num_paths);
190 		  for (i = 0; i < _plotter->drawstate->num_paths; i++)
191 		    {
192 
193 		      if (merged_paths[i] == (plPath *)NULL)
194 			continue;
195 
196 		      _plotter->drawstate->path = merged_paths[i];
197 		      _plotter->paint_path (S___(_plotter));
198 		      if (merged_paths[i] != _plotter->drawstate->paths[i])
199 			_delete_plPath (merged_paths[i]);
200 		    }
201 		  _plotter->drawstate->path = (plPath *)NULL;
202 		}
203 
204 	      if (pen_type)
205 		/* edge the compound path, i.e., edge each of its simple
206                    paths */
207 		{
208 		  _plotter->drawstate->pen_type = pen_type;
209 		  _plotter->drawstate->fill_type = 0; /* unfilled */
210 		  for (i = 0; i < _plotter->drawstate->num_paths; i++)
211 		    {
212 		      _plotter->drawstate->path = _plotter->drawstate->paths[i];
213 		      _plotter->paint_path (S___(_plotter));
214 		    }
215 		  _plotter->drawstate->path = (plPath *)NULL;
216 		}
217 
218 	      /* restore filling/edging attributes */
219 	      _plotter->drawstate->fill_type = fill_type;
220 	      _plotter->drawstate->pen_type = pen_type;
221 	    }
222 	}
223     }
224 
225   /* compound path is now painted, so remove it from paths buffer */
226   for (i = 0; i < _plotter->drawstate->num_paths; i++)
227     _delete_plPath (_plotter->drawstate->paths[i]);
228   free (_plotter->drawstate->paths);
229   _plotter->drawstate->paths = (plPath **)NULL;
230   _plotter->drawstate->num_paths = 0;
231 
232   return 0;
233 }
234 
235 int
_API_endsubpath(S___ (Plotter * _plotter))236 _API_endsubpath (S___(Plotter *_plotter))
237 {
238   if (!_plotter->data->open)
239     {
240       _plotter->error (R___(_plotter)
241 		       "endsubpath: invalid operation");
242       return -1;
243     }
244 
245   if (_plotter->drawstate->path)
246     /* have a simple path under construction, so move it to list of stored
247        simple paths */
248     {
249       if (_plotter->drawstate->num_paths == 0)
250 	_plotter->drawstate->paths =
251 	  (plPath **)_pl_xmalloc(sizeof (plPath *));
252       else
253 	_plotter->drawstate->paths =
254 	  (plPath **)_pl_xrealloc(_plotter->drawstate->paths,
255 				    (_plotter->drawstate->num_paths + 1)
256 				    * sizeof (plPath *));
257       _plotter->drawstate->paths[_plotter->drawstate->num_paths++] =
258 	_plotter->drawstate->path;
259       _plotter->drawstate->path = (plPath *)NULL;
260     }
261 
262   return 0;
263 }
264 
265 int
_API_closepath(S___ (Plotter * _plotter))266 _API_closepath (S___(Plotter *_plotter))
267 {
268   if (!_plotter->data->open)
269     {
270       _plotter->error (R___(_plotter)
271 		       "closepath: invalid operation");
272       return -1;
273     }
274 
275   /* NOT YET IMPLEMENTED */
276 
277   return 0;
278 }
279 
280 
281