1 /* gEDA - GPL Electronic Design Automation
2  * libgeda - gEDA's library
3  * Copyright (C) 1998-2010 Ales Hvezda
4  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <math.h>
24 #include <string.h>
25 
26 #include "libgeda_priv.h"
27 
28 #ifdef HAVE_LIBDMALLOC
29 #include <dmalloc.h>
30 #endif
31 
32 
33 typedef void (*DRAW_FUNC) (TOPLEVEL *toplevel, FILE *fp, PATH *path,
34                            int line_width, int length, int space,
35                            int origin_x, int origin_y);
36 
37 
38 typedef void (*FILL_FUNC) (TOPLEVEL *toplevel, FILE *fp, PATH *path,
39                            int fill_width,
40                            int angle1, int pitch1, int angle2, int pitch2,
41                            int origin_x, int origin_y);
42 
43 
44 /*! \brief Create and add path OBJECT to list.
45  *  \par Function Description
46  *  This function creates a new object representing a path.
47  *  This object is added to the end of the list <B>object_list</B>
48  *  pointed object belongs to.
49  *  The path is described by its two ends - <B>x1</B>,<B>y1</B> and
50  *  <B>x2</B>,<B>y2</B>.
51  *  The <B>type</B> parameter must be equal to #OBJ_PATH.
52  *  The <B>color</B> parameter corresponds to the color the path
53  *  will be drawn with.
54  *
55  *  The #OBJECT structure is allocated with the
56  *  #s_basic_init_object() function. The structure describing
57  *  the path is allocated and initialized with the parameters given
58  *  to the function.
59  *
60  *  Both the path type and the filling type are set to default
61  *  values : solid path type with a width of 0, and no filling.
62  *  It can be changed after with the #o_set_line_options() and
63  *  #o_set_fill_options().
64  *
65  *  The object is added to the end of the list described by the
66  *  <B>object_list</B> parameter by the #s_basic_link_object().
67  *
68  *  \param [in]     toplevel     The TOPLEVEL object.
69  *  \param [in]     type         Must be OBJ_PATH.
70  *  \param [in]     color        The path color.
71  *  \param [in]     path_string  The string representation of the path
72  *  \return A pointer to the new end of the object list.
73  */
o_path_new(TOPLEVEL * toplevel,char type,int color,const char * path_string)74 OBJECT *o_path_new (TOPLEVEL *toplevel,
75                     char type, int color, const char *path_string)
76 {
77   OBJECT *new_node;
78 
79   /* create the object */
80   new_node        = s_basic_new_object (type, "path");
81   new_node->color = color;
82 
83   new_node->path  = s_path_parse (path_string);
84 
85   /* path type and filling initialized to default */
86   o_set_line_options (toplevel, new_node,
87                       END_NONE, TYPE_SOLID, 0, -1, -1);
88   o_set_fill_options (toplevel, new_node,
89                       FILLING_HOLLOW, -1, -1, -1, -1, -1);
90 
91   /* compute bounding box */
92   o_path_recalc (toplevel, new_node);
93 
94   return new_node;
95 }
96 
97 
98 /*! \brief Create a copy of a path.
99  *  \par Function Description
100  *  This function creates a verbatim copy of the
101  *  object pointed by <B>o_current</B> describing a path. The new object
102  *  is added at the end of the list following the <B>list_tail</B>
103  *  parameter.
104  *
105  *  \param [in]  toplevel  The TOPLEVEL object.
106  *  \param [in]  o_current  Line OBJECT to copy.
107  *  \return A new pointer to the end of the object list.
108  */
o_path_copy(TOPLEVEL * toplevel,OBJECT * o_current)109 OBJECT *o_path_copy (TOPLEVEL *toplevel, OBJECT *o_current)
110 {
111   OBJECT *new_obj;
112   char *path_string;
113 
114   path_string = s_path_string_from_path (o_current->path);
115   new_obj = o_path_new (toplevel, OBJ_PATH, o_current->color, path_string);
116   g_free (path_string);
117 
118   /* copy the path type and filling options */
119   o_set_line_options (toplevel, new_obj, o_current->line_end,
120                       o_current->line_type, o_current->line_width,
121                       o_current->line_length, o_current->line_space);
122   o_set_fill_options (toplevel, new_obj,
123                       o_current->fill_type, o_current->fill_width,
124                       o_current->fill_pitch1, o_current->fill_angle1,
125                       o_current->fill_pitch2, o_current->fill_angle2);
126 
127   /* calc the bounding box */
128   o_path_recalc (toplevel, o_current);
129 
130   /* return the new tail of the object list */
131   return new_obj;
132 }
133 
134 
135 /*! \brief Create path OBJECT from character string.
136  *  \par Function Description
137  *  This function creates a path OBJECT from the character string
138  *  <B>*buf</B> and a number of lines following that describing the
139  *  path, read from <B>*tb</B>.
140  *
141  *  Depending on <B>*version</B>, the correct file format is considered.
142  *  Currently two file format revisions are supported :
143  *  <DL>
144  *    <DT>*</DT><DD>the file format used until 20010704 release.
145  *    <DT>*</DT><DD>the file format used for the releases after 20010704.
146  *  </DL>
147  *
148  *  \param [in]  toplevel       The TOPLEVEL object.
149  *  \param [in]  first_line      Character string with path description.
150  *  \param [in]  tb              Text buffer containing the path string.
151  *  \param [in]  release_ver     libgeda release version number.
152  *  \param [in]  fileformat_ver  libgeda file format version number.
153  *  \return A pointer to the new path object, or NULL on error;
154  */
o_path_read(TOPLEVEL * toplevel,const char * first_line,TextBuffer * tb,unsigned int release_ver,unsigned int fileformat_ver,GError ** err)155 OBJECT *o_path_read (TOPLEVEL *toplevel,
156                      const char *first_line, TextBuffer *tb,
157                      unsigned int release_ver, unsigned int fileformat_ver, GError **err)
158 {
159   OBJECT *new_obj;
160   char type;
161   int color;
162   int line_width, line_space, line_length;
163   int line_end;
164   int line_type;
165   int fill_type, fill_width, angle1, pitch1, angle2, pitch2;
166   int num_lines = 0;
167   int i;
168   char *string;
169   GString *pathstr;
170 
171   /*
172    * The current path format to describe a line is a space separated
173    * list of characters and numbers in plain ASCII on a single path.
174    * The meaning of each item is described in the file format documentation.
175    */
176   /* Allocate enough space */
177   if (sscanf (first_line, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
178 	      &type, &color, &line_width, &line_end, &line_type,
179 	      &line_length, &line_space, &fill_type, &fill_width, &angle1,
180 	      &pitch1, &angle2, &pitch2, &num_lines) != 14) {
181     g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse path object"));
182     return NULL;
183   }
184 
185   /*
186    * Checks if the required color is valid.
187    */
188   if (color < 0 || color > MAX_COLORS) {
189     s_log_message (_("Found an invalid color [ %s ]\n"), first_line);
190     s_log_message (_("Setting color to default color\n"));
191     color = DEFAULT_COLOR;
192   }
193 
194   /*
195    * A path is internally described by its two ends. A new object is
196    * allocated, initialized and added to the list of objects. Its path
197    * type is set according to the values of the fields on the path.
198    */
199 
200   pathstr = g_string_new ("");
201   for (i = 0; i < num_lines; i++) {
202     const gchar *line;
203 
204     line = s_textbuffer_next_line (tb);
205 
206     if (line == NULL) {
207       g_set_error (err, EDA_ERROR, EDA_ERROR_PARSE, _("Unexpected end-of-file when reading path"));
208       return NULL;
209     }
210 
211     pathstr = g_string_append (pathstr, line);
212   }
213 
214   /* retrieve the character string from the GString */
215   string = g_string_free (pathstr, FALSE);
216   string = remove_last_nl (string);
217 
218   /* create a new path */
219   new_obj = o_path_new (toplevel, type, color, string);
220   g_free (string);
221 
222   /* set its line options */
223   o_set_line_options (toplevel, new_obj,
224                       line_end, line_type, line_width, line_length, line_space);
225   /* set its fill options */
226   o_set_fill_options (toplevel, new_obj,
227                       fill_type, fill_width, pitch1, angle1, pitch2, angle2);
228 
229   return new_obj;
230 }
231 
232 
233 /*! \brief Create a character string representation of a path OBJECT.
234  *  \par Function Description
235  *  The function formats a string in the buffer <B>*buff</B> to describe
236  *  the path object <B>*object</B>.
237  *
238  *  \param [in] toplevel  a TOPLEVEL structure
239  *  \param [in] object  path OBJECT to create string from.
240  *  \return A pointer to the path OBJECT character string.
241  *
242  *  \note
243  *  Caller must g_free returned character string.
244  *
245  */
o_path_save(TOPLEVEL * toplevel,OBJECT * object)246 char *o_path_save (TOPLEVEL *toplevel, OBJECT *object)
247 {
248   int line_width, line_space, line_length;
249   char *buf;
250   int num_lines;
251   OBJECT_END line_end;
252   OBJECT_TYPE line_type;
253   OBJECT_FILLING fill_type;
254   int fill_width, angle1, pitch1, angle2, pitch2;
255   char *path_string;
256 
257   /* description of the line type */
258   line_width  = object->line_width;
259   line_end    = object->line_end;
260   line_type   = object->line_type;
261   line_length = object->line_length;
262   line_space  = object->line_space;
263 
264   /* filling parameters */
265   fill_type    = object->fill_type;
266   fill_width   = object->fill_width;
267   angle1       = object->fill_angle1;
268   pitch1       = object->fill_pitch1;
269   angle2       = object->fill_angle2;
270   pitch2       = object->fill_pitch2;
271 
272   path_string = s_path_string_from_path (object->path);
273   num_lines = o_text_num_lines (path_string);
274   buf = g_strdup_printf ("%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n%s",
275                          object->type, object->color, line_width, line_end,
276                          line_type, line_length, line_space, fill_type,
277                          fill_width, angle1, pitch1, angle2, pitch2,
278                          num_lines, path_string);
279   g_free (path_string);
280 
281   return buf;
282 }
283 
284 
285 /*! \brief Modify controol point location
286  *
287  *  \par Function Description
288  *  This function modifies a control point location of the path object
289  *  *object. The control point being modified is selected according to
290  *  the whichone parameter.
291  *
292  *  The new position is given by <B>x</B> and <B>y</B>.
293  *
294  *  \param [in]     toplevel  The TOPLEVEL object.
295  *  \param [in,out] object    The path OBJECT
296  *  \param [in]     x         New x coordinate for the control point
297  *  \param [in]     y         New y coordinate for the control point
298  *  \param [in]     whichone  Which control point is being modified
299  */
o_path_modify(TOPLEVEL * toplevel,OBJECT * object,int x,int y,int whichone)300 void o_path_modify (TOPLEVEL *toplevel, OBJECT *object,
301                     int x, int y, int whichone)
302 {
303   int i;
304   int grip_no = 0;
305   PATH_SECTION *section;
306 
307   o_emit_pre_change_notify (toplevel, object);
308 
309   for (i = 0; i <  object->path->num_sections; i++) {
310     section = &object->path->sections[i];
311 
312     switch (section->code) {
313     case PATH_CURVETO:
314       /* Two control point grips */
315       if (whichone == grip_no++) {
316         section->x1 = x;
317         section->y1 = y;
318       }
319       if (whichone == grip_no++) {
320         section->x2 = x;
321         section->y2 = y;
322       }
323       /* Fall through */
324     case PATH_MOVETO:
325     case PATH_MOVETO_OPEN:
326     case PATH_LINETO:
327       /* Destination point grip */
328       if (whichone == grip_no++) {
329         section->x3 = x;
330         section->y3 = y;
331       }
332       break;
333     case PATH_END:
334       break;
335     }
336   }
337 
338   /* Update bounding box */
339   o_path_recalc (toplevel, object);
340   o_emit_change_notify (toplevel, object);
341 }
342 
343 
344 /*! \brief Translate a path position in WORLD coordinates by a delta.
345  *  \par Function Description
346  *  This function applies a translation of (<B>x1</B>,<B>y1</B>) to the path
347  *  described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world unit.
348  *
349  *  \param [in]     toplevel  The TOPLEVEL object.
350  *  \param [in]     dx         x distance to move.
351  *  \param [in]     dy         y distance to move.
352  *  \param [in,out] object     Line OBJECT to translate.
353  */
o_path_translate_world(TOPLEVEL * toplevel,int dx,int dy,OBJECT * object)354 void o_path_translate_world (TOPLEVEL *toplevel,
355                              int dx, int dy, OBJECT *object)
356 {
357   PATH_SECTION *section;
358   int i;
359 
360   for (i = 0; i < object->path->num_sections; i++) {
361     section = &object->path->sections[i];
362 
363     switch (section->code) {
364     case PATH_CURVETO:
365       section->x1 += dx;
366       section->y1 += dy;
367       section->x2 += dx;
368       section->y2 += dy;
369       /* Fall through */
370     case PATH_MOVETO:
371     case PATH_MOVETO_OPEN:
372     case PATH_LINETO:
373       section->x3 += dx;
374       section->y3 += dy;
375       break;
376     case PATH_END:
377       break;
378     }
379   }
380 
381   /* Update bounding box */
382   o_path_recalc (toplevel, object);
383 }
384 
385 
386 /*! \brief Rotate Line OBJECT using WORLD coordinates.
387  *  \par Function Description
388  *  This function rotates the path described by
389  *  <B>*object</B> around the (<B>world_centerx</B>,<B>world_centery</B>)
390  *  point by <B>angle</B> degrees.
391  *  The center of rotation is in world units.
392  *
393  *  \param [in]      toplevel      The TOPLEVEL object.
394  *  \param [in]      world_centerx  Rotation center x coordinate in WORLD units.
395  *  \param [in]      world_centery  Rotation center y coordinate in WORLD units.
396  *  \param [in]      angle          Rotation angle in degrees (See note below).
397  *  \param [in,out]  object         Line OBJECT to rotate.
398  */
o_path_rotate_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,int angle,OBJECT * object)399 void o_path_rotate_world (TOPLEVEL *toplevel,
400                           int world_centerx, int world_centery, int angle,
401                           OBJECT *object)
402 {
403   PATH_SECTION *section;
404   int i;
405 
406   for (i = 0; i < object->path->num_sections; i++) {
407     section = &object->path->sections[i];
408 
409     switch (section->code) {
410     case PATH_CURVETO:
411       /* Two control point grips */
412       section->x1 -= world_centerx; section->y1 -= world_centery;
413       section->x2 -= world_centerx; section->y2 -= world_centery;
414       rotate_point_90 (section->x1, section->y1, angle, &section->x1, &section->y1);
415       rotate_point_90 (section->x2, section->y2, angle, &section->x2, &section->y2);
416       section->x1 += world_centerx; section->y1 += world_centery;
417       section->x2 += world_centerx; section->y2 += world_centery;
418       /* Fall through */
419     case PATH_MOVETO:
420     case PATH_MOVETO_OPEN:
421     case PATH_LINETO:
422       /* Destination point grip */
423       section->x3 -= world_centerx; section->y3 -= world_centery;
424       rotate_point_90 (section->x3, section->y3, angle, &section->x3, &section->y3);
425       section->x3 += world_centerx; section->y3 += world_centery;
426       break;
427     case PATH_END:
428       break;
429     }
430   }
431   o_path_recalc (toplevel, object);
432 }
433 
434 
435 /*! \brief Mirror a path using WORLD coordinates.
436  *  \par Function Description
437  *  This function mirrors the path from the point
438  *  (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
439  *
440  *  \param [in]     toplevel      The TOPLEVEL object.
441  *  \param [in]     world_centerx  Origin x coordinate in WORLD units.
442  *  \param [in]     world_centery  Origin y coordinate in WORLD units.
443  *  \param [in,out] object         Line OBJECT to mirror.
444  */
o_path_mirror_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,OBJECT * object)445 void o_path_mirror_world (TOPLEVEL *toplevel, int world_centerx,
446                           int world_centery, OBJECT *object)
447 {
448   PATH_SECTION *section;
449   int i;
450 
451   for (i = 0; i < object->path->num_sections; i++) {
452     section = &object->path->sections[i];
453 
454     switch (section->code) {
455     case PATH_CURVETO:
456       /* Two control point grips */
457       section->x1 = 2 * world_centerx - section->x1;
458       section->x2 = 2 * world_centerx - section->x2;
459       /* Fall through */
460     case PATH_MOVETO:
461     case PATH_MOVETO_OPEN:
462     case PATH_LINETO:
463       /* Destination point grip */
464       section->x3 = 2 * world_centerx - section->x3;
465       break;
466     case PATH_END:
467       break;
468     }
469   }
470 
471   o_path_recalc (toplevel, object);
472 }
473 
474 
475 /*! \brief Recalculate path coordinates in SCREEN units.
476  *  \par Function Description
477  *  This function recalculate the bounding box of the <B>o_current</B>
478  *
479  *  \param [in] toplevel      The TOPLEVEL object.
480  *  \param [in,out] o_current  Line OBJECT to be recalculated.
481  */
o_path_recalc(TOPLEVEL * toplevel,OBJECT * o_current)482 void o_path_recalc (TOPLEVEL *toplevel, OBJECT *o_current)
483 {
484   int left = 0, right = 0, top = 0, bottom = 0;
485 
486   g_return_if_fail (o_current->path != NULL);
487 
488   /* Update the bounding box */
489   if (o_current->path->num_sections > 0) {
490     world_get_path_bounds (toplevel, o_current, &left, &top, &right, &bottom);
491     o_current->w_left   = left;
492     o_current->w_top    = top;
493     o_current->w_right  = right;
494     o_current->w_bottom = bottom;
495     o_current->w_bounds_valid = TRUE;
496   } else {
497     o_current->w_bounds_valid = FALSE;
498   }
499 }
500 
501 
502 /*! \brief Get path bounding rectangle in WORLD coordinates.
503  *  \par Function Description
504  *  This function sets the <B>left</B>, <B>top</B>, <B>right</B> and
505  *  <B>bottom</B> parameters to the boundings of the path object described
506  *  in <B>*path</B> in world units.
507  *
508  *  \note Bounding box for bezier curves is loose because we just consider
509  *        the convex hull of the curve control and end-points.
510  *
511  *  \param [in]  toplevel  The TOPLEVEL object.
512  *  \param [in]  object     Line OBJECT to read coordinates from.
513  *  \param [out] left       Left path coordinate in WORLD units.
514  *  \param [out] top        Top path coordinate in WORLD units.
515  *  \param [out] right      Right path coordinate in WORLD units.
516  *  \param [out] bottom     Bottom path coordinate in WORLD units.
517  */
world_get_path_bounds(TOPLEVEL * toplevel,OBJECT * object,int * left,int * top,int * right,int * bottom)518 void world_get_path_bounds (TOPLEVEL *toplevel, OBJECT *object,
519                             int *left, int *top, int *right, int *bottom)
520 {
521   PATH_SECTION *section;
522   int halfwidth;
523   int i;
524   int found_bound = FALSE;
525 
526   /* Find the bounds of the path region */
527   for (i = 0; i < object->path->num_sections; i++) {
528     section = &object->path->sections[i];
529     switch (section->code) {
530       case PATH_CURVETO:
531         /* Bezier curves with this construction of control points will lie
532          * within the convex hull of the control and curve end points */
533         *left   = (found_bound) ? MIN (*left,   section->x1) : section->x1;
534         *top    = (found_bound) ? MIN (*top,    section->y1) : section->y1;
535         *right  = (found_bound) ? MAX (*right,  section->x1) : section->x1;
536         *bottom = (found_bound) ? MAX (*bottom, section->y1) : section->y1;
537         found_bound = TRUE;
538         *left   = MIN (*left,   section->x2);
539         *top    = MIN (*top,    section->y2);
540         *right  = MAX (*right,  section->x2);
541         *bottom = MAX (*bottom, section->y2);
542         /* Fall through */
543       case PATH_MOVETO:
544       case PATH_MOVETO_OPEN:
545       case PATH_LINETO:
546         *left   = (found_bound) ? MIN (*left,   section->x3) : section->x3;
547         *top    = (found_bound) ? MIN (*top,    section->y3) : section->y3;
548         *right  = (found_bound) ? MAX (*right,  section->x3) : section->x3;
549         *bottom = (found_bound) ? MAX (*bottom, section->y3) : section->y3;
550         found_bound = TRUE;
551         break;
552       case PATH_END:
553         break;
554     }
555   }
556 
557   if (found_bound) {
558     /* This isn't strictly correct, but a 1st order approximation */
559     halfwidth = object->line_width / 2;
560     *left   -= halfwidth;
561     *top    -= halfwidth;
562     *right  += halfwidth;
563     *bottom += halfwidth;
564   }
565 }
566 
567 /*! \brief get the position of the first path point
568  *  \par Function Description
569  *  This function gets the position of the first point of an path object.
570  *
571  *  \param [in] toplevel The toplevel environment.
572  *  \param [out] x       pointer to the x-position
573  *  \param [out] y       pointer to the y-position
574  *  \param [in] object   The object to get the position.
575  *  \return TRUE if successfully determined the position, FALSE otherwise
576  */
o_path_get_position(TOPLEVEL * toplevel,gint * x,gint * y,OBJECT * object)577 gboolean o_path_get_position (TOPLEVEL *toplevel, gint *x, gint *y,
578                               OBJECT *object)
579 {
580   if (object->path->num_sections == 0)
581     return FALSE;
582 
583   *x = object->path->sections[0].x3;
584   *y = object->path->sections[0].y3;
585   return TRUE;
586 }
587 
588 /*! \brief Print a solid PATH to Postscript document.
589  *  \par Function Description
590  *  This function prints the outline of a path when a solid line type is
591  *  required. The postscript file is defined by the file pointer <B>fp</B>.
592  *  The parameters <B>length</B> and <B>space</B> are ignored.
593  *
594  *  All dimensions are in mils.
595  *
596  *  \param [in] toplevel   The TOPLEVEL object.
597  *  \param [in] fp          FILE pointer to Postscript document.
598  *  \param [in] path        The PATH object ot print
599  *  \param [in] line_width  PATH Line width.
600  *  \param [in] length      Dashed line length.
601  *  \param [in] space       Amount of space between dashes.
602  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT.
603  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT.
604  */
o_path_print_solid(TOPLEVEL * toplevel,FILE * fp,PATH * path,int line_width,int length,int space,int origin_x,int origin_y)605 static void o_path_print_solid (TOPLEVEL *toplevel, FILE *fp, PATH *path,
606                                 int line_width, int length, int space,
607                                 int origin_x, int origin_y)
608 {
609   int i;
610 
611   for (i = 0; i < path->num_sections; i++) {
612     PATH_SECTION *section = &path->sections[i];
613 
614     if (i > 0)
615       fprintf (fp, " ");
616 
617     switch (section->code) {
618       case PATH_MOVETO:
619         fprintf (fp, "closepath");
620         /* Fall through */
621       case PATH_MOVETO_OPEN:
622         fprintf (fp, "%i %i moveto",
623                      section->x3 - origin_x, section->y3 - origin_y);
624         break;
625       case PATH_CURVETO:
626         fprintf (fp, "%i %i %i %i %i %i curveto",
627                      section->x1 - origin_x, section->y1 - origin_y,
628                      section->x2 - origin_x, section->y2 - origin_y,
629                      section->x3 - origin_x, section->y3 - origin_y);
630         break;
631       case PATH_LINETO:
632         fprintf (fp, "%i %i lineto",
633                      section->x3 - origin_x, section->y3 - origin_y);
634         break;
635       case PATH_END:
636         fprintf (fp, "closepath");
637         break;
638     }
639   }
640 
641   fprintf (fp, " stroke\n");
642 }
643 
644 
645 /*! \brief Print a dotted PATH to Postscript document.
646  *  \par Function Description
647  *  This function prints the outline of a path when a dotted line type is
648  *  required. The postscript file is defined by the file pointer <B>fp</B>.
649  *  The parameter <B>length</B> is ignored.
650  *
651  *  All dimensions are in mils.
652  *
653  *  \param [in] toplevel   The TOPLEVEL object
654  *  \param [in] fp          FILE pointer to Postscript document
655  *  \param [in] path        The PATH object to print
656  *  \param [in] line_width  PATH Line width
657  *  \param [in] length      Dashed line length
658  *  \param [in] space       Amount of space between dashes
659  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT
660  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT
661  */
o_path_print_dotted(TOPLEVEL * toplevel,FILE * fp,PATH * path,int line_width,int length,int space,int origin_x,int origin_y)662 static void o_path_print_dotted (TOPLEVEL *toplevel, FILE *fp, PATH *path,
663                                  int line_width, int length, int space,
664                                  int origin_x, int origin_y)
665 {
666   o_path_print_solid (toplevel, fp, path, line_width,
667                       length, space, origin_x, origin_y);
668 }
669 
670 
671 /*! \brief Print a dashed PATH to Postscript document.
672  *  \par Function Description
673  *  This function prints the outline of a path when a dashed line type is
674  *  required. The postscript file is defined by the file pointer <B>fp</B>.
675  *
676  *  All dimensions are in mils.
677  *
678  *  \param [in] toplevel    The TOPLEVEL object.
679  *  \param [in] fp          FILE pointer to Postscript document.
680  *  \param [in] path        The PATH object to print.
681  *  \param [in] line_width  PATH Line width.
682  *  \param [in] length      Dashed line length.
683  *  \param [in] space       Amount of space between dashes.
684  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT.
685  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT.
686  */
o_path_print_dashed(TOPLEVEL * toplevel,FILE * fp,PATH * path,int line_width,int length,int space,int origin_x,int origin_y)687 static void o_path_print_dashed (TOPLEVEL *toplevel, FILE *fp, PATH *path,
688                                  int line_width, int length, int space,
689                                  int origin_x, int origin_y)
690 {
691   o_path_print_solid (toplevel, fp, path, line_width,
692                       length, space, origin_x, origin_y);
693 }
694 
695 
696 /*! \brief Print centered line type PATH to Postscript document.
697  *  \par Function Description
698  *  This function prints the outline of a path when a centered line type is
699  *  required. The postscript file is defined by the file pointer <B>fp</B>.
700  *
701  *  All dimensions are in mils.
702  *
703  *  \param [in] toplevel    The TOPLEVEL object
704  *  \param [in] fp          FILE pointer to Postscript document
705  *  \param [in] path        The PATH object to print
706  *  \param [in] line_width  PATH Line width
707  *  \param [in] length      Dashed line length
708  *  \param [in] space       Amount of space between dashes
709  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT
710  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT
711  */
o_path_print_center(TOPLEVEL * toplevel,FILE * fp,PATH * path,int line_width,int length,int space,int origin_x,int origin_y)712 static void o_path_print_center (TOPLEVEL *toplevel, FILE *fp, PATH *path,
713                                  int line_width, int length,
714                                  int space, int origin_x, int origin_y)
715 {
716   o_path_print_solid (toplevel, fp, path, line_width,
717                       length, space, origin_x, origin_y);
718 }
719 
720 
721 /*! \brief Print phantom line type PATH to Postscript document.
722  *  \par Function Description
723  *  This function prints the outline of a path when a phantom line type is
724  *  required. The postscript file is defined by the file pointer <B>fp</B>.
725  *
726  *  All dimensions are in mils.
727  *
728  *  \param [in] toplevel    The TOPLEVEL object
729  *  \param [in] fp          FILE pointer to Postscript document
730  *  \param [in] path        The PATH object to print
731  *  \param [in] line_width  PATH Line width
732  *  \param [in] length      Dashed line length
733  *  \param [in] space       Amount of space between dashes
734  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT
735  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT
736  */
o_path_print_phantom(TOPLEVEL * toplevel,FILE * fp,PATH * path,int line_width,int length,int space,int origin_x,int origin_y)737 static void o_path_print_phantom (TOPLEVEL *toplevel, FILE *fp, PATH *path,
738                                   int line_width, int length,
739                                   int space, int origin_x, int origin_y)
740 {
741   o_path_print_solid (toplevel, fp, path, line_width,
742                       length, space, origin_x, origin_y);
743 }
744 
745 
746 /*! \brief Print a solid pattern PATH to Postscript document.
747  *  \par Function Description
748  *  The function prints a filled path with a solid pattern. No outline is
749  *  printed. The postscript file is defined by the file pointer <B>fp</B>.
750  *  <B>fill_width</B>, <B>angle1</B> and <B>pitch1</B>, <B>angle2</B> and <B>pitch2</B>
751  *  parameters are ignored in this functions but kept for compatibility
752  *  with other fill functions.
753  *
754  *  All dimensions are in mils.
755  *
756  *  \param [in] toplevel    The TOPLEVEL object
757  *  \param [in] fp          FILE pointer to Postscript document
758  *  \param [in] path        The PATH object to print
759  *  \param [in] fill_width  PATH fill width (unused)
760  *  \param [in] angle1      (unused)
761  *  \param [in] pitch1      (unused)
762  *  \param [in] angle2      (unused)
763  *  \param [in] pitch2      (unused)
764  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT
765  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT
766  */
o_path_print_filled(TOPLEVEL * toplevel,FILE * fp,PATH * path,int fill_width,int angle1,int pitch1,int angle2,int pitch2,int origin_x,int origin_y)767 static void o_path_print_filled (TOPLEVEL *toplevel, FILE *fp, PATH *path,
768                                  int fill_width,
769                                  int angle1, int pitch1, int angle2, int pitch2,
770                                  int origin_x, int origin_y)
771 {
772   int i;
773 
774   for (i = 0; i < path->num_sections; i++) {
775     PATH_SECTION *section = &path->sections[i];
776 
777     if (i > 0)
778       fprintf (fp, " ");
779 
780     switch (section->code) {
781       case PATH_MOVETO:
782         fprintf (fp, "closepath");
783         /* Fall through */
784       case PATH_MOVETO_OPEN:
785         fprintf (fp, "%i %i moveto",
786                      section->x3 - origin_x, section->y3 - origin_y);
787         break;
788       case PATH_CURVETO:
789         fprintf (fp, "%i %i %i %i %i %i curveto",
790                      section->x1 - origin_x, section->y1 - origin_y,
791                      section->x2 - origin_x, section->y2 - origin_y,
792                      section->x3 - origin_x, section->y3 - origin_y);
793         break;
794       case PATH_LINETO:
795         fprintf (fp, "%i %i lineto",
796                      section->x3 - origin_x, section->y3 - origin_y);
797         break;
798       case PATH_END:
799         fprintf (fp, "closepath");
800         break;
801     }
802   }
803 
804   fprintf (fp, " fill\n");
805 }
806 
807 
808 /*! \brief Print a hatch pattern PATH to Postscript document.
809  *  \par Function Description
810  *  The function prints a hatched path. No outline is printed.
811  *  The postscript file is defined by the file pointer <B>fp</B>.
812  *  <B>fill_width</B>, <B>angle1</B>, <B>pitch1</B> parameters define the way the path
813  *  has to be hatched.
814  *  <B>angle2</B> and <B>pitch2</B> parameters are unused but kept for compatibility
815  *  with other fill functions.
816  *
817  *  Negative or zero values for <B>pitch1</B> are not allowed.
818  *
819  *  All dimensions are in mils.
820  *
821  *  \param [in] toplevel    The TOPLEVEL object
822  *  \param [in] fp          FILE pointer to Postscript document
823  *  \param [in] path        The PATH object to print
824  *  \param [in] fill_width  PATH fill width
825  *  \param [in] angle1      Angle of hatch pattern
826  *  \param [in] pitch1      Pitch of hatch pattern
827  *  \param [in] angle2      (unused)
828  *  \param [in] pitch2      (unused)
829  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT
830  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT
831  */
o_path_print_hatch(TOPLEVEL * toplevel,FILE * fp,PATH * path,int fill_width,int angle1,int pitch1,int angle2,int pitch2,int origin_x,int origin_y)832 static void o_path_print_hatch (TOPLEVEL *toplevel, FILE *fp, PATH *path,
833                                 int fill_width,
834                                 int angle1, int pitch1, int angle2, int pitch2,
835                                 int origin_x, int origin_y)
836 {
837   int i;
838   GArray *lines;
839 
840   g_return_if_fail (toplevel != NULL);
841   g_return_if_fail (fp != NULL);
842 
843   /* Avoid printing line widths too small */
844   if (fill_width <= 1) fill_width = 2;
845 
846   lines = g_array_new (FALSE, FALSE, sizeof(LINE));
847 
848   m_hatch_path (path, angle1, pitch1, lines);
849 
850   for (i=0; i < lines->len; i++) {
851     LINE *line = &g_array_index (lines, LINE, i);
852 
853     fprintf (fp,"%d %d %d %d %d line\n", line->x[0], line->y[0],
854                                          line->x[1], line->y[1], fill_width);
855   }
856 
857   g_array_free (lines, TRUE);
858 }
859 
860 
861 /*! \brief Print a mesh pattern PATH to Postscript document.
862  *  \par Function Description
863  *  This function prints a meshed path. No outline is printed.
864  *  The postscript file is defined by the file pointer <B>fp</B>.
865  *
866  *  Negative or zero values for <B>pitch1</B> and/or <B>pitch2</B> are
867  *  not allowed.
868  *
869  *  All dimensions are in mils.
870  *
871  *  \param [in] toplevel    The TOPLEVEL object
872  *  \param [in] fp          FILE pointer to Postscript document
873  *  \param [in] path        The PATH object to print
874  *  \param [in] fill_width  PATH fill width
875  *  \param [in] angle1      1st angle for mesh pattern
876  *  \param [in] pitch1      1st pitch for mesh pattern
877  *  \param [in] angle2      2nd angle for mesh pattern
878  *  \param [in] pitch2      2nd pitch for mesh pattern
879  *  \param [in] origin_x    Page x coordinate to place PATH OBJECT
880  *  \param [in] origin_y    Page y coordinate to place PATH OBJECT
881  */
o_path_print_mesh(TOPLEVEL * toplevel,FILE * fp,PATH * path,int fill_width,int angle1,int pitch1,int angle2,int pitch2,int origin_x,int origin_y)882 static void o_path_print_mesh (TOPLEVEL *toplevel, FILE *fp, PATH *path,
883                                int fill_width,
884                                int angle1, int pitch1, int angle2, int pitch2,
885                                int origin_x, int origin_y)
886 {
887   o_path_print_hatch (toplevel, fp, path, fill_width,
888                       angle1, pitch1, -1, -1, origin_x, origin_y);
889 
890   o_path_print_hatch (toplevel, fp, path, fill_width,
891                       angle2, pitch2, -1, -1, origin_x, origin_y);
892 }
893 
894 
895 /*! \brief Print PATH to Postscript document.
896  *  \par Function Description
897  *  This function prints the path described by the <B>o_current</B>
898  *  parameter to a Postscript document.
899  *  The Postscript document is descibed by the file pointer <B>fp</B>.
900  *
901  *  \param [in] toplevel  The TOPLEVEL object.
902  *  \param [in] fp         FILE pointer to Postscript document.
903  *  \param [in] o_current  PATH OBJECT to write to document.
904  *  \param [in] origin_x   Page x coordinate to place PATH OBJECT.
905  *  \param [in] origin_y   Page y coordinate to place PATH OBJECT.
906  */
o_path_print(TOPLEVEL * toplevel,FILE * fp,OBJECT * o_current,int origin_x,int origin_y)907 void o_path_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
908                   int origin_x, int origin_y)
909 {
910   int line_width, length, space;
911   int fill_width, angle1, pitch1, angle2, pitch2;
912   DRAW_FUNC outl_func = NULL;
913   FILL_FUNC fill_func = NULL;
914 
915   /*! \note
916    *  Depending on the type of the line for this particular path, the
917    *  appropriate function is chosen among #o_path_print_solid(),
918    *  #o_path_print_dotted(), #o_path_print_dashed(),
919    *  #o_path_print_center() and #o_path_print_phantom().
920    *
921    *  The needed parameters for each of these type is extracted from the
922    *  <B>o_current</B> object. Depending on the type, unused parameters are
923    *  set to -1.
924    *
925    *  In the eventuality of a length and/or space null, the line is printed
926    *  solid to avoid and endless loop produced by other functions in such a
927    *  case.
928    */
929   line_width = o_current->line_width;
930 
931   if (line_width <= 2) {
932     if (toplevel->line_style == THICK) {
933       line_width = LINE_WIDTH;
934     } else {
935       line_width=2;
936     }
937   }
938   length = o_current->line_length;
939   space  = o_current->line_space;
940 
941   switch(o_current->line_type) {
942     case TYPE_SOLID:
943       length = -1; space  = -1;
944       outl_func = o_path_print_solid;
945       break;
946 
947     case TYPE_DOTTED:
948       length = -1;
949       outl_func = o_path_print_dotted;
950       break;
951 
952     case TYPE_DASHED:
953       outl_func = o_path_print_dashed;
954       break;
955 
956     case TYPE_CENTER:
957       outl_func = o_path_print_center;
958       break;
959 
960     case TYPE_PHANTOM:
961       outl_func = o_path_print_phantom;
962       break;
963 
964     case TYPE_ERASE:
965       /* Unused for now, print it solid */
966       length = -1; space  = -1;
967       outl_func = o_path_print_solid;
968       break;
969   }
970 
971   if((length == 0) || (space == 0)) {
972     length = -1; space  = -1;
973     outl_func = o_path_print_solid;
974   }
975 
976   f_print_set_color (toplevel, fp, o_current->color);
977 
978   f_print_set_line_width (fp, line_width);
979 
980   (*outl_func) (toplevel, fp, o_current->path, line_width,
981                 length, space, origin_x, origin_y);
982 
983   /*! \note
984    *  If the filling type of the path is not <B>HOLLOW</B>, the appropriate
985    *  function is chosen among #o_path_print_filled(), #o_path_print_mesh()
986    *  and #o_path_print_hatch(). The corresponding parameters are extracted
987    *  from the <B>o_current</B> object and corrected afterward.
988    *
989    *  The case where <B>pitch1</B> and <B>pitch2</B> are null or negative is
990    *  avoided as it leads to an endless loop in most of the called functions.
991    *  In such a case, the path is printed filled. Unused parameters for each of
992    *  these functions are set to -1 or any passive value.
993    */
994   if(o_current->fill_type != FILLING_HOLLOW) {
995     fill_width = o_current->fill_width;
996     angle1     = o_current->fill_angle1;
997     pitch1     = o_current->fill_pitch1;
998     angle2     = o_current->fill_angle2;
999     pitch2     = o_current->fill_pitch2;
1000 
1001     switch(o_current->fill_type) {
1002       case FILLING_FILL:
1003         angle1 = -1; pitch1 = 1;
1004         angle2 = -1; pitch2 = 1;
1005         fill_width = -1;
1006         fill_func = o_path_print_filled;
1007         break;
1008 
1009       case FILLING_MESH:
1010         fill_func = o_path_print_mesh;
1011         break;
1012 
1013       case FILLING_HATCH:
1014         angle2 = -1; pitch2 = 1;
1015         fill_func = o_path_print_hatch;
1016         break;
1017 
1018       case FILLING_VOID:
1019         /* Unused for now, print it filled */
1020         angle1 = -1; pitch1 = 1;
1021         angle2 = -1; pitch2 = 1;
1022         fill_width = -1;
1023         fill_func = o_path_print_filled;
1024         break;
1025 
1026       case FILLING_HOLLOW:
1027         /* nop */
1028         break;
1029 
1030     }
1031 
1032     if((pitch1 <= 0) || (pitch2 <= 0)) {
1033       angle1 = -1; pitch1 = 1;
1034       angle2 = -1; pitch2 = 1;
1035       fill_func = o_path_print_filled;
1036     }
1037 
1038     (*fill_func) (toplevel, fp,
1039                   o_current->path, fill_width,
1040                   angle1, pitch1, angle2, pitch2, origin_x, origin_y);
1041   }
1042 }
1043 
1044 
1045 /*! \brief Calculates the distance between the given point and the closest
1046  *  point on the given path segment.
1047  *
1048  *  \param [in] object       The path OBJECT.
1049  *  \param [in] x            The x coordinate of the given point.
1050  *  \param [in] y            The y coordinate of the given point.
1051  *  \param [in] force_solid  If true, force treating the object as solid.
1052  *  \return The shortest distance from the object to the point.  With an
1053  *  invalid parameter, this function returns G_MAXDOUBLE.
1054  */
o_path_shortest_distance(OBJECT * object,int x,int y,int force_solid)1055 double o_path_shortest_distance (OBJECT *object, int x, int y, int force_solid)
1056 {
1057   int solid;
1058 
1059   solid = force_solid || object->fill_type != FILLING_HOLLOW;
1060 
1061   return s_path_shortest_distance (object->path, x, y, solid);
1062 }
1063 
1064