1 /* Lepton EDA library
2  * Copyright (C) 1998-2010 Ales Hvezda
3  * Copyright (C) 1998-2016 gEDA Contributors
4  * Copyright (C) 2017-2021 Lepton EDA Contributors
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 "liblepton_priv.h"
27 
28 /*! \file path_object.c
29  *  \brief functions for the path object
30  */
31 
32 /*! \brief Return path object section with given number.
33  *  \par Function Description
34  *  Returns the #LeptonPathSection object pointer with the number
35  *  \a i for #LeptonObject.
36  *
37  *  \param [in] object The #LeptonObject.
38  *  \param [in] i The id number of wanted section.
39  *  \return The pointer to the requested #LeptonPathSection.
40  */
41 LeptonPathSection*
lepton_path_object_get_section(const LeptonObject * object,int i)42 lepton_path_object_get_section (const LeptonObject *object,
43                                 int i)
44 {
45   g_return_val_if_fail (lepton_object_is_path (object), NULL);
46   g_return_val_if_fail (object->path != NULL, NULL);
47 
48   g_return_val_if_fail ((i >= 0) &&
49                         (i < lepton_path_object_get_num_sections (object)),
50                         NULL);
51 
52   return &object->path->sections[i];
53 }
54 
55 
56 /*! \brief Create a new path object.
57  *  \par Function Description
58  *  This function creates a new object representing a path.
59  *  This object is added to the end of the list <B>object_list</B>
60  *  pointed object belongs to.
61  *  The <B>type</B> parameter must be equal to #OBJ_PATH.
62  *  The <B>color</B> parameter corresponds to the color the path
63  *  will be drawn with.
64  *  The path shape is created by parsing \a path_string.
65  *
66  *  The #LeptonObject structure is allocated with the
67  *  #lepton_object_new() function. The structure describing the
68  *  path is allocated and initialized with the parameters given to
69  *  the function.
70  *
71  *  Both the path type and the filling type are set to default
72  *  values : solid path type with a width of 0, and no filling.
73  *  It can be changed after with the #lepton_object_set_line_options() and
74  *  #lepton_object_set_fill_options().
75  *
76  *  The object is added to the end of the list described by the
77  *  <B>object_list</B> parameter by the #s_basic_link_object().
78  *
79  *  \param [in]     color        The path color.
80  *  \param [in]     path_string  The string representation of the path
81  *  \return A pointer to the new end of the object list.
82  */
83 LeptonObject*
lepton_path_object_new(int color,const char * path_string)84 lepton_path_object_new (int color,
85                         const char *path_string)
86 {
87   return lepton_path_object_new_take_path (color,
88                                            lepton_path_parse (path_string));
89 }
90 
91 
92 /*! \brief Create a new path object.
93  *  \par Function Description
94  *  This function creates and returns a new LeptonObject representing a path
95  *  using the path shape data stored in \a path_data.  The \a
96  *  path_data is subsequently owned by the returned LeptonObject.
97  *
98  *  \see lepton_path_object_new().
99  *
100  *  \param [in]     color        The path color.
101  *  \param [in]     path_data    The #LeptonPath data structure to use.
102  *  \return A pointer to the new end of the object list.
103  */
104 LeptonObject*
lepton_path_object_new_take_path(int color,LeptonPath * path_data)105 lepton_path_object_new_take_path (int color,
106                                   LeptonPath *path_data)
107 {
108   LeptonObject *new_node;
109 
110   /* create the object */
111   new_node        = lepton_object_new (OBJ_PATH, "path");
112   lepton_object_set_color (new_node, color);
113 
114   new_node->path  = path_data;
115 
116   /* path type and filling initialized to default */
117   lepton_object_set_line_options (new_node,
118                                   DEFAULT_OBJECT_END,
119                                   TYPE_SOLID,
120                                   LINE_WIDTH,
121                                   -1,
122                                   -1);
123 
124   lepton_object_set_fill_options (new_node,
125                                   FILLING_HOLLOW,
126                                   -1,
127                                   -1,
128                                   -1,
129                                   -1,
130                                   -1);
131 
132   return new_node;
133 }
134 
135 
136 /*! \brief Create a copy of a path.
137  *  \par Function Description
138  *  This function creates a verbatim copy of the
139  *  object pointed by <B>o_current</B> describing a path. The new object
140  *  is added at the end of the list following the <B>list_tail</B>
141  *  parameter.
142  *
143  *  \param [in]  o_current  Line LeptonObject to copy.
144  *  \return A new pointer to the end of the object list.
145  */
146 LeptonObject*
lepton_path_object_copy(LeptonObject * o_current)147 lepton_path_object_copy (LeptonObject *o_current)
148 {
149   LeptonObject *new_obj;
150   char *path_string;
151 
152   path_string = lepton_path_string_from_path (o_current->path);
153   new_obj = lepton_path_object_new (lepton_object_get_color (o_current),
154                                     path_string);
155   g_free (path_string);
156 
157   /* copy the path type and filling options */
158   lepton_object_set_line_options (new_obj,
159                                   lepton_object_get_stroke_cap_type (o_current),
160                                   lepton_object_get_stroke_type (o_current),
161                                   lepton_object_get_stroke_width (o_current),
162                                   lepton_object_get_stroke_dash_length (o_current),
163                                   lepton_object_get_stroke_space_length (o_current));
164   lepton_object_set_fill_options (new_obj,
165                                   lepton_object_get_fill_type (o_current),
166                                   lepton_object_get_fill_width (o_current),
167                                   lepton_object_get_fill_pitch1 (o_current),
168                                   lepton_object_get_fill_angle1 (o_current),
169                                   lepton_object_get_fill_pitch2 (o_current),
170                                   lepton_object_get_fill_angle2 (o_current));
171 
172   /* return the new tail of the object list */
173   return new_obj;
174 }
175 
176 
177 /*! \brief Create path LeptonObject from character string.
178  *  \par Function Description
179  *  This function creates a path LeptonObject from the character string
180  *  <B>*buf</B> and a number of lines following that describing the
181  *  path, read from <B>*tb</B>.
182  *
183  *  Depending on <B>*version</B>, the correct file format is considered.
184  *  Currently two file format revisions are supported :
185  *  <DL>
186  *    <DT>*</DT><DD>the file format used until 20010704 release.
187  *    <DT>*</DT><DD>the file format used for the releases after 20010704.
188  *  </DL>
189  *
190  *  \param [in]  first_line      Character string with path description.
191  *  \param [in]  tb              Text buffer containing the path string.
192  *  \param [in]  release_ver     libgeda release version number.
193  *  \param [in]  fileformat_ver  libgeda file format version number.
194  *  \return A pointer to the new path object, or NULL on error;
195  */
196 LeptonObject*
o_path_read(const char * first_line,TextBuffer * tb,unsigned int release_ver,unsigned int fileformat_ver,GError ** err)197 o_path_read (const char *first_line,
198              TextBuffer *tb,
199              unsigned int release_ver,
200              unsigned int fileformat_ver,
201              GError **err)
202 {
203   LeptonObject *new_obj;
204   char type;
205   int color;
206   int line_width, line_space, line_length;
207   int line_end;
208   int line_type;
209   int fill_type, fill_width, angle1, pitch1, angle2, pitch2;
210   int num_lines = 0;
211   int i;
212   char *string;
213   GString *pathstr;
214 
215   /*
216    * The current path format to describe a line is a space separated
217    * list of characters and numbers in plain ASCII on a single path.
218    * The meaning of each item is described in the file format documentation.
219    */
220   /* Allocate enough space */
221   if (sscanf (first_line, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
222               &type, &color, &line_width, &line_end, &line_type,
223               &line_length, &line_space, &fill_type, &fill_width, &angle1,
224               &pitch1, &angle2, &pitch2, &num_lines) != 14) {
225     g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse path object"));
226     return NULL;
227   }
228 
229   /*
230    * Checks if the required color is valid.
231    */
232   if (!color_id_valid (color)) {
233     g_message (_("Found an invalid color [ %1$s ]"), first_line);
234     g_message (_("Setting color to default color."));
235     color = default_color_id();
236   }
237 
238   /*
239    * A path is internally described by its two ends. A new object is
240    * allocated, initialized and added to the list of objects. Its path
241    * type is set according to the values of the fields on the path.
242    */
243 
244   pathstr = g_string_new ("");
245   for (i = 0; i < num_lines; i++) {
246     const gchar *line;
247 
248     line = s_textbuffer_next_line (tb);
249 
250     if (line == NULL) {
251       g_set_error (err, EDA_ERROR, EDA_ERROR_PARSE, _("Unexpected end-of-file when reading path"));
252       return NULL;
253     }
254 
255     pathstr = g_string_append (pathstr, line);
256   }
257 
258   /* retrieve the character string from the GString */
259   string = g_string_free (pathstr, FALSE);
260   string = lepton_str_remove_ending_newline (string);
261 
262   /* create a new path */
263   new_obj = lepton_path_object_new (color, string);
264   g_free (string);
265 
266   /* set its line options */
267   lepton_object_set_line_options (new_obj,
268                                   (LeptonStrokeCapType) line_end,
269                                   (LeptonStrokeType) line_type,
270                                   line_width,
271                                   line_length,
272                                   line_space);
273   /* set its fill options */
274   lepton_object_set_fill_options (new_obj,
275                                   (LeptonFillType) fill_type,
276                                   fill_width,
277                                   pitch1,
278                                   angle1,
279                                   pitch2,
280                                   angle2);
281 
282   return new_obj;
283 }
284 
285 
286 /*! \brief Create a character string representation of a path LeptonObject.
287  *  \par Function Description
288  *  The function formats a string in the buffer <B>*buff</B> to describe
289  *  the path object <B>*object</B>.
290  *
291  *  \param [in] object  path LeptonObject to create string from.
292  *  \return A pointer to the path LeptonObject character string.
293  *
294  *  \note
295  *  Caller must g_free returned character string.
296  *
297  */
298 gchar*
lepton_path_object_to_buffer(const LeptonObject * object)299 lepton_path_object_to_buffer (const LeptonObject *object)
300 {
301   int line_width, line_space, line_length;
302   char *buf;
303   int num_lines;
304   LeptonStrokeCapType line_end;
305   LeptonStrokeType line_type;
306   LeptonFillType fill_type;
307   int fill_width, angle1, pitch1, angle2, pitch2;
308   char *path_string;
309 
310   /* description of the line type */
311   line_width  = lepton_object_get_stroke_width (object);
312   line_end    = lepton_object_get_stroke_cap_type (object);
313   line_type   = lepton_object_get_stroke_type (object);
314   line_length = lepton_object_get_stroke_dash_length (object);
315   line_space  = lepton_object_get_stroke_space_length (object);
316 
317   /* filling parameters */
318   fill_type    = lepton_object_get_fill_type (object);
319   fill_width   = lepton_object_get_fill_width (object);
320   angle1       = lepton_object_get_fill_angle1 (object);
321   pitch1       = lepton_object_get_fill_pitch1 (object);
322   angle2       = lepton_object_get_fill_angle2 (object);
323   pitch2       = lepton_object_get_fill_pitch2 (object);
324 
325   path_string = lepton_path_string_from_path (object->path);
326   num_lines = o_text_num_lines (path_string);
327   buf = g_strdup_printf ("%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n%s",
328                          lepton_object_get_type (object),
329                          lepton_object_get_color (object),
330                          line_width, line_end,
331                          line_type, line_length, line_space, fill_type,
332                          fill_width, angle1, pitch1, angle2, pitch2,
333                          num_lines, path_string);
334   g_free (path_string);
335 
336   return buf;
337 }
338 
339 
340 /*! \brief Modify controol point location
341  *
342  *  \par Function Description
343  *  This function modifies a control point location of the path object
344  *  *object. The control point being modified is selected according to
345  *  the whichone parameter.
346  *
347  *  The new position is given by <B>x</B> and <B>y</B>.
348  *
349  *  \param [in,out] object    The path LeptonObject
350  *  \param [in]     x         New x coordinate for the control point
351  *  \param [in]     y         New y coordinate for the control point
352  *  \param [in]     whichone  Which control point is being modified
353  */
354 void
lepton_path_object_modify(LeptonObject * object,int x,int y,int whichone)355 lepton_path_object_modify (LeptonObject *object,
356                            int x,
357                            int y,
358                            int whichone)
359 {
360   int i;
361   int grip_no = 0;
362   LeptonPathSection *section;
363 
364   lepton_object_emit_pre_change_notify (object);
365 
366   for (i = 0; i < lepton_path_object_get_num_sections (object); i++)
367   {
368     section = lepton_path_object_get_section (object, i);
369 
370     switch (section->code) {
371     case PATH_CURVETO:
372       /* Two control point grips */
373       if (whichone == grip_no++) {
374         section->x1 = x;
375         section->y1 = y;
376       }
377       if (whichone == grip_no++) {
378         section->x2 = x;
379         section->y2 = y;
380       }
381       /* Fall through */
382     case PATH_MOVETO:
383     case PATH_MOVETO_OPEN:
384     case PATH_LINETO:
385       /* Destination point grip */
386       if (whichone == grip_no++) {
387         section->x3 = x;
388         section->y3 = y;
389       }
390       break;
391     case PATH_END:
392       break;
393     }
394   }
395 
396   lepton_object_emit_change_notify (object);
397 }
398 
399 
400 /*! \brief Translate a path position in WORLD coordinates by a delta.
401  *  \par Function Description
402  *  This function applies a translation of (<B>x1</B>,<B>y1</B>) to the path
403  *  described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world unit.
404  *
405  *  \param [in,out] object     Line LeptonObject to translate.
406  *  \param [in]     dx         x distance to move.
407  *  \param [in]     dy         y distance to move.
408  */
409 void
lepton_path_object_translate(LeptonObject * object,int dx,int dy)410 lepton_path_object_translate (LeptonObject *object,
411                               int dx,
412                               int dy)
413 {
414   LeptonPathSection *section;
415   int i;
416 
417   g_return_if_fail (lepton_object_is_path (object));
418   g_return_if_fail (object->path != NULL);
419 
420   for (i = 0; i < lepton_path_object_get_num_sections (object); i++)
421   {
422     section = lepton_path_object_get_section (object, i);
423 
424     switch (section->code) {
425     case PATH_CURVETO:
426       section->x1 += dx;
427       section->y1 += dy;
428       section->x2 += dx;
429       section->y2 += dy;
430       /* Fall through */
431     case PATH_MOVETO:
432     case PATH_MOVETO_OPEN:
433     case PATH_LINETO:
434       section->x3 += dx;
435       section->y3 += dy;
436       break;
437     case PATH_END:
438       break;
439     }
440   }
441 }
442 
443 
444 /*! \brief Rotate Line LeptonObject using WORLD coordinates.
445  *  \par Function Description
446  *  This function rotates the path described by
447  *  <B>*object</B> around the (<B>world_centerx</B>,<B>world_centery</B>)
448  *  point by <B>angle</B> degrees.
449  *  The center of rotation is in world units.
450  *
451  *  \param [in]      world_centerx  Rotation center x coordinate in WORLD units.
452  *  \param [in]      world_centery  Rotation center y coordinate in WORLD units.
453  *  \param [in]      angle          Rotation angle in degrees (See note below).
454  *  \param [in,out]  object         Line LeptonObject to rotate.
455  */
456 void
lepton_path_object_rotate(int world_centerx,int world_centery,int angle,LeptonObject * object)457 lepton_path_object_rotate (int world_centerx,
458                            int world_centery,
459                            int angle,
460                            LeptonObject *object)
461 {
462   LeptonPathSection *section;
463   int i;
464 
465   g_return_if_fail (lepton_object_is_path (object));
466   g_return_if_fail (object->path != NULL);
467 
468   for (i = 0; i < lepton_path_object_get_num_sections (object); i++)
469   {
470     section = lepton_path_object_get_section (object, i);
471 
472     switch (section->code) {
473     case PATH_CURVETO:
474       /* Two control point grips */
475       section->x1 -= world_centerx; section->y1 -= world_centery;
476       section->x2 -= world_centerx; section->y2 -= world_centery;
477       lepton_point_rotate_90 (section->x1, section->y1, angle, &section->x1, &section->y1);
478       lepton_point_rotate_90 (section->x2, section->y2, angle, &section->x2, &section->y2);
479       section->x1 += world_centerx; section->y1 += world_centery;
480       section->x2 += world_centerx; section->y2 += world_centery;
481       /* Fall through */
482     case PATH_MOVETO:
483     case PATH_MOVETO_OPEN:
484     case PATH_LINETO:
485       /* Destination point grip */
486       section->x3 -= world_centerx; section->y3 -= world_centery;
487       lepton_point_rotate_90 (section->x3,
488                               section->y3,
489                               angle,
490                               &section->x3,
491                               &section->y3);
492       section->x3 += world_centerx; section->y3 += world_centery;
493       break;
494     case PATH_END:
495       break;
496     }
497   }
498 }
499 
500 
501 /*! \brief Mirror a path using WORLD coordinates.
502  *  \par Function Description
503  *  This function mirrors the path from the point
504  *  (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
505  *
506  *  \param [in]     world_centerx  Origin x coordinate in WORLD units.
507  *  \param [in]     world_centery  Origin y coordinate in WORLD units.
508  *  \param [in,out] object         Line LeptonObject to mirror.
509  */
510 void
lepton_path_object_mirror(int world_centerx,int world_centery,LeptonObject * object)511 lepton_path_object_mirror (int world_centerx,
512                            int world_centery,
513                            LeptonObject *object)
514 {
515   LeptonPathSection *section;
516   int i;
517 
518   g_return_if_fail (lepton_object_is_path (object));
519   g_return_if_fail (object->path != NULL);
520 
521   for (i = 0; i < lepton_path_object_get_num_sections (object); i++)
522   {
523     section = lepton_path_object_get_section (object, i);
524 
525     switch (section->code) {
526     case PATH_CURVETO:
527       /* Two control point grips */
528       section->x1 = 2 * world_centerx - section->x1;
529       section->x2 = 2 * world_centerx - section->x2;
530       /* Fall through */
531     case PATH_MOVETO:
532     case PATH_MOVETO_OPEN:
533     case PATH_LINETO:
534       /* Destination point grip */
535       section->x3 = 2 * world_centerx - section->x3;
536       break;
537     case PATH_END:
538       break;
539     }
540   }
541 }
542 
543 
544 /*! \brief Calculate the bounds of the path
545  *
546  *  On failure, this function sets the bounds to empty.
547  *
548  *  \param [in]  object    The path to calculate bounds of.
549  *  \param [out] bounds    The bounds of the path
550  */
551 void
lepton_path_object_calculate_bounds(const LeptonObject * object,LeptonBounds * bounds)552 lepton_path_object_calculate_bounds (const LeptonObject *object,
553                                      LeptonBounds *bounds)
554 {
555   gint expand;
556   gint i;
557 
558   lepton_bounds_init (bounds);
559 
560   g_return_if_fail (lepton_object_is_path (object));
561   g_return_if_fail (object->path != NULL);
562 
563   /* Find the bounds of the path region */
564   for (i = 0; i < lepton_path_object_get_num_sections (object); i++)
565   {
566     LeptonPathSection *section = lepton_path_object_get_section (object, i);
567 
568     switch (section->code) {
569       case PATH_CURVETO:
570         /* Bezier curves with this construction of control points will lie
571          * within the convex hull of the control and curve end points */
572         bounds->min_x = MIN (bounds->min_x, section->x1);
573         bounds->min_y = MIN (bounds->min_y, section->y1);
574         bounds->max_x = MAX (bounds->max_x, section->x1);
575         bounds->max_y = MAX (bounds->max_y, section->y1);
576         bounds->min_x = MIN (bounds->min_x, section->x2);
577         bounds->min_y = MIN (bounds->min_y, section->y2);
578         bounds->max_x = MAX (bounds->max_x, section->x2);
579         bounds->max_y = MAX (bounds->max_y, section->y2);
580         /* Fall through */
581       case PATH_MOVETO:
582       case PATH_MOVETO_OPEN:
583       case PATH_LINETO:
584         bounds->min_x = MIN (bounds->min_x, section->x3);
585         bounds->min_y = MIN (bounds->min_y, section->y3);
586         bounds->max_x = MAX (bounds->max_x, section->x3);
587         bounds->max_y = MAX (bounds->max_y, section->y3);
588         break;
589       case PATH_END:
590         break;
591     }
592   }
593 
594   expand = ceil (0.5 * G_SQRT2 * lepton_object_get_stroke_width (object));
595 
596   /* This isn't strictly correct, but a 1st order approximation */
597   lepton_bounds_expand (bounds, bounds, expand, expand);
598 }
599 
600 /*! \brief get the position of the first path point
601  *  \par Function Description
602  *  This function gets the position of the first point of an path object.
603  *
604  *  \param [in] object   The object to get the position.
605  *  \param [out] x       pointer to the x-position
606  *  \param [out] y       pointer to the y-position
607  *  \return TRUE if successfully determined the position, FALSE otherwise
608  */
609 gboolean
lepton_path_object_get_position(const LeptonObject * object,gint * x,gint * y)610 lepton_path_object_get_position (const LeptonObject *object,
611                                  gint *x,
612                                  gint *y)
613 {
614   g_return_val_if_fail (lepton_object_is_path (object), FALSE);
615   g_return_val_if_fail (object->path != NULL, FALSE);
616 
617   if (lepton_path_object_get_num_sections (object) == 0)
618   {
619     return FALSE;
620   }
621 
622   if (x != NULL) {
623     *x = object->path->sections[0].x3;
624   }
625 
626   if (y != NULL) {
627     *y = object->path->sections[0].y3;
628   }
629 
630   return TRUE;
631 }
632 
633 /*! \brief Calculates the distance between the given point and the closest
634  *  point on the given path segment.
635  *
636  *  \param [in] object         The path LeptonObject.
637  *  \param [in] x              The x coordinate of the given point.
638  *  \param [in] y              The y coordinate of the given point.
639  *  \param [in] force_solid    If true, force treating the object as solid.
640  *  \param [in] include_hidden Take hidden text into account.
641  *  \return The shortest distance from the object to the point.  With an
642  *  invalid parameter, this function returns G_MAXDOUBLE.
643  */
644 double
lepton_path_object_shortest_distance(LeptonObject * object,int x,int y,int force_solid,gboolean include_hidden)645 lepton_path_object_shortest_distance (LeptonObject *object,
646                                       int x,
647                                       int y,
648                                       int force_solid,
649                                       gboolean include_hidden)
650 {
651   g_return_val_if_fail (lepton_object_is_path (object), G_MAXDOUBLE);
652   g_return_val_if_fail (object->path != NULL, G_MAXDOUBLE);
653 
654   int solid;
655 
656   solid = force_solid ||
657     lepton_object_get_fill_type (object) != FILLING_HOLLOW;
658 
659   return lepton_path_shortest_distance (object->path, x, y, solid);
660 }
661 
662 
663 /*! \brief Insert a new path section into path object.
664  *  \par Function description
665  *  Given the object #LeptonObject, section #LeptonPathSection,
666  *  and position id \a i, inserts the section into the object in
667  *  specified position.  If the position number is less than zero
668  *  or greater than the number of path object sections, appends
669  *  the section to the end of the path.
670  *
671  *  \param [in] object  The path #LeptonObject to insert section into.
672  *  \param [in] section The #LeptonPathSection to insert.
673  *  \param [in] i       The position id of the new section in the path.
674  *  \return The modified #LeptonObject.
675  */
676 LeptonObject*
lepton_path_object_insert_section(LeptonObject * object,LeptonPathSection * section,int i)677 lepton_path_object_insert_section (LeptonObject *object,
678                                    LeptonPathSection *section,
679                                    int i)
680 {
681   LeptonPath *path = object->path;
682 
683   /* Start making changes. */
684   lepton_object_emit_pre_change_notify (object);
685 
686   /* Make sure there's enough space for the new element. */
687   if (path->num_sections == path->num_sections_max) {
688     path->sections =
689       (LeptonPathSection*) g_realloc (path->sections,
690                                       (path->num_sections_max <<= 1) *
691                                       sizeof (LeptonPathSection));
692   }
693 
694   /* Move path contents to make a gap in the right place. */
695   if ((i < 0) || (i > path->num_sections)) {
696     i = path->num_sections;
697   } else {
698     memmove (&path->sections[i+1], &path->sections[i],
699              sizeof (LeptonPathSection) * (path->num_sections - i));
700   }
701 
702   path->num_sections++;
703   path->sections[i] = *section;
704 
705   lepton_object_emit_change_notify (object);
706 
707   return object;
708 }
709 
710 
711 /*! \brief Remove a path section from path object.
712  *  \par Function description
713  *  Given the object #LeptonObject, and the section number \a i,
714  *  removes the section with the specified number from the path
715  *  object and returns the modified object.
716  *
717  *  \param [in] object  The path #LeptonObject to remove section from.
718  *  \param [in] i       The id number of the section to remove.
719  *  \return The modified #LeptonObject.
720  */
721 LeptonObject*
lepton_path_object_remove_section(LeptonObject * object,int i)722 lepton_path_object_remove_section (LeptonObject *object,
723                                    int i)
724 {
725   int num_sections = lepton_path_object_get_num_sections (object);
726 
727   if ((i < 0) || (i >= num_sections))
728   {
729     /* Index is invalid for path.  Return the object unchanged. */
730     return object;
731   }
732 
733   lepton_object_emit_pre_change_notify (object);
734 
735   /* If section is not last in path, remove section at index by
736    * moving all sections above index one location down. */
737   if (i + 1 != num_sections)
738   {
739     memmove (&object->path->sections[i],
740              &object->path->sections[i+1],
741              sizeof (LeptonPathSection) * (num_sections - i - 1));
742   }
743 
744   /* Decrement the number of sections. */
745   lepton_path_object_set_num_sections (object, num_sections - 1);
746 
747   lepton_object_emit_change_notify (object);
748 
749   return object;
750 }
751 
752 /*! \brief Get the number of sections of path object.
753  *
754  *  \param [in] object The path #LeptonObject.
755  *  \return The number of sections.
756  */
757 int
lepton_path_object_get_num_sections(const LeptonObject * object)758 lepton_path_object_get_num_sections (const LeptonObject *object)
759 {
760   g_return_val_if_fail (lepton_object_is_path (object), 0);
761   g_return_val_if_fail (object->path != NULL, 0);
762 
763   return object->path->num_sections;
764 }
765 
766 /*! \brief Set the number of sections of path object.
767  *
768  *  \param [in] object The path #LeptonObject.
769  *  \param [in] num The new number of sections.
770  */
771 void
lepton_path_object_set_num_sections(LeptonObject * object,int num)772 lepton_path_object_set_num_sections (LeptonObject *object,
773                                      int num)
774 {
775   g_return_if_fail (lepton_object_is_path (object));
776   g_return_if_fail (object->path != NULL);
777 
778   object->path->num_sections = num;
779 }
780