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, §ion->x1, §ion->y1);
478 lepton_point_rotate_90 (section->x2, section->y2, angle, §ion->x2, §ion->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 §ion->x3,
491 §ion->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