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
21 /*! \file line_object.c
22 * \brief functions for the line object
23 */
24
25 #include <config.h>
26
27 #include <stdio.h>
28 #include <math.h>
29
30 #include "liblepton_priv.h"
31
32 /*! \brief Create and add line LeptonObject to list.
33 * \par Function Description
34 * This function creates a new object representing a line.
35 *
36 * The line is described by its two ends - <B>x1</B>,<B>y1</B> and
37 * <B>x2</B>,<B>y2</B>.
38 * The <B>type</B> parameter must be equal to #OBJ_LINE.
39 * The <B>color</B> parameter corresponds to the color the box
40 * will be drawn with.
41 *
42 * The #LeptonObject structure is allocated with the #lepton_object_new()
43 * function. The structure describing the line is allocated and
44 * initialized with the parameters given to the function.
45 *
46 * Both the line type and the filling type are set to default
47 * values : solid line type with a width of 0, and no filling.
48 * It can be changed after with the #lepton_object_set_line_options() and
49 * #lepton_object_set_fill_options().
50 *
51 * \param [in] type Must be OBJ_LINE.
52 * \param [in] color Circle line color.
53 * \param [in] x1 Upper x coordinate.
54 * \param [in] y1 Upper y coordinate.
55 * \param [in] x2 Lower x coordinate.
56 * \param [in] y2 Lower y coordinate.
57 * \return A pointer to the new end of the object list.
58 */
59 LeptonObject*
lepton_line_object_new(gint color,gint x1,gint y1,gint x2,gint y2)60 lepton_line_object_new (gint color,
61 gint x1,
62 gint y1,
63 gint x2,
64 gint y2)
65 {
66 LeptonObject *new_node;
67
68 /* create the object */
69 new_node = lepton_object_new (OBJ_LINE, "line");
70 lepton_object_set_color (new_node, color);
71
72 new_node->line = lepton_line_new ();
73
74 /* describe the line with its two ends */
75 new_node->line->x[0] = x1;
76 new_node->line->y[0] = y1;
77 new_node->line->x[1] = x2;
78 new_node->line->y[1] = y2;
79
80 /* line type and filling initialized to default */
81 lepton_object_set_line_options (new_node,
82 DEFAULT_OBJECT_END,
83 TYPE_SOLID,
84 LINE_WIDTH,
85 -1,
86 -1);
87
88 lepton_object_set_fill_options (new_node,
89 FILLING_HOLLOW,
90 -1,
91 -1,
92 -1,
93 -1,
94 -1);
95
96 return new_node;
97 }
98
99 /*! \brief Create a copy of a line.
100 * \par Function Description
101 * This function creates a verbatim copy of the
102 * object pointed by <B>o_current</B> describing a line.
103 *
104 * \param [in] o_current Line LeptonObject to copy.
105 * \return The new LeptonObject
106 */
107 LeptonObject*
lepton_line_object_copy(LeptonObject * o_current)108 lepton_line_object_copy (LeptonObject *o_current)
109 {
110 LeptonObject *new_obj;
111
112 new_obj = lepton_line_object_new (lepton_object_get_color (o_current),
113 o_current->line->x[0],
114 o_current->line->y[0],
115 o_current->line->x[1],
116 o_current->line->y[1]);
117
118 /* copy the line type and filling options */
119 lepton_object_set_line_options (new_obj,
120 lepton_object_get_stroke_cap_type (o_current),
121 lepton_object_get_stroke_type (o_current),
122 lepton_object_get_stroke_width (o_current),
123 lepton_object_get_stroke_dash_length (o_current),
124 lepton_object_get_stroke_space_length (o_current));
125
126 lepton_object_set_fill_options (new_obj,
127 lepton_object_get_fill_type (o_current),
128 lepton_object_get_fill_width (o_current),
129 lepton_object_get_fill_pitch1 (o_current),
130 lepton_object_get_fill_angle1 (o_current),
131 lepton_object_get_fill_pitch2 (o_current),
132 lepton_object_get_fill_angle2 (o_current));
133
134 return new_obj;
135 }
136
137 /*! \brief Get the x coordinate of first endpoint
138 *
139 * The coordinate properties are broken out individually to make it easier for
140 * the GUI. This way, the GUI does not need as many adapters to interface to
141 * a line boxed type.
142 *
143 * \param [in] object The line
144 * \return The x coordinate for the first endpoint
145 */
146 gint
lepton_line_object_get_x0(const LeptonObject * object)147 lepton_line_object_get_x0 (const LeptonObject *object)
148 {
149 g_return_val_if_fail (object != NULL, 0);
150 g_return_val_if_fail (object->line != NULL, 0);
151
152 return object->line->x[0];
153 }
154
155 /*! \brief Get the x coordinate of second endpoint
156 *
157 * The coordinate properties are broken out individually to make it easier for
158 * the GUI. This way, the GUI does not need as many adapters to interface to
159 * a line boxed type.
160 *
161 * \param [in] object The line
162 * \return The x coordinate for the second endpoint
163 */
164 gint
lepton_line_object_get_x1(const LeptonObject * object)165 lepton_line_object_get_x1 (const LeptonObject *object)
166 {
167 g_return_val_if_fail (object != NULL, 0);
168 g_return_val_if_fail (object->line != NULL, 0);
169
170 return object->line->x[1];
171 }
172
173 /*! \brief Get the y coordinate of first endpoint
174 *
175 * The coordinate properties are broken out individually to make it easier for
176 * the GUI. This way, the GUI does not need as many adapters to interface to
177 * a line boxed type.
178 *
179 * \param [in] object The line
180 * \return The y coordinate for the first endpoint
181 */
182 gint
lepton_line_object_get_y0(const LeptonObject * object)183 lepton_line_object_get_y0 (const LeptonObject *object)
184 {
185 g_return_val_if_fail (object != NULL, 0);
186 g_return_val_if_fail (object->line != NULL, 0);
187
188 return object->line->y[0];
189 }
190
191 /*! \brief Get the y coordinate of second endpoint
192 *
193 * The coordinate properties are broken out individually to make it easier for
194 * the GUI. This way, the GUI does not need as many adapters to interface to
195 * a line boxed type.
196 *
197 * \param [in] object The line
198 * \return The y coordinate for the second endpoint
199 */
200 gint
lepton_line_object_get_y1(const LeptonObject * object)201 lepton_line_object_get_y1 (const LeptonObject *object)
202 {
203 g_return_val_if_fail (object != NULL, 0);
204 g_return_val_if_fail (object->line != NULL, 0);
205
206 return object->line->y[1];
207 }
208
209 /*! \brief Set the x coordinate of first endpoint
210 *
211 * The coordinate properties are broken out individually to make it easier for
212 * the GUI. This way, the GUI does not need as many adapters to interface to
213 * a line boxed type.
214 *
215 * \param [in,out] object The line
216 * \param [in] x The new x coordinate for the first endpoint
217 */
218 void
lepton_line_object_set_x0(LeptonObject * object,gint x)219 lepton_line_object_set_x0 (LeptonObject *object,
220 gint x)
221 {
222 g_return_if_fail (object != NULL);
223 g_return_if_fail (object->line != NULL);
224
225 object->line->x[0] = x;
226 }
227
228 /*! \brief Set the x coordinate of second endpoint
229 *
230 * The coordinate properties are broken out individually to make it easier for
231 * the GUI. This way, the GUI does not need as many adapters to interface to
232 * a line boxed type.
233 *
234 * \param [in,out] object The line
235 * \param [in] x The new x coordinate for the second endpoint
236 */
237 void
lepton_line_object_set_x1(LeptonObject * object,gint x)238 lepton_line_object_set_x1 (LeptonObject *object,
239 gint x)
240 {
241 g_return_if_fail (object != NULL);
242 g_return_if_fail (object->line != NULL);
243
244 object->line->x[1] = x;
245 }
246
247 /*! \brief Set the y coordinate of first endpoint
248 *
249 * The coordinate properties are broken out individually to make it easier for
250 * the GUI. This way, the GUI does not need as many adapters to interface to
251 * a line boxed type.
252 *
253 * \param [in,out] object The line
254 * \param [in] y The new y coordinate for the first endpoint
255 */
256 void
lepton_line_object_set_y0(LeptonObject * object,gint y)257 lepton_line_object_set_y0 (LeptonObject *object,
258 gint y)
259 {
260 g_return_if_fail (object != NULL);
261 g_return_if_fail (object->line != NULL);
262
263 object->line->y[0] = y;
264 }
265
266 /*! \brief Set the y coordinate of second endpoint
267 *
268 * The coordinate properties are broken out individually to make it easier for
269 * the GUI. This way, the GUI does not need as many adapters to interface to
270 * a line boxed type.
271 *
272 * \param [in,out] object The line
273 * \param [in] y The new y coordinate for the second endpoint
274 */
275 void
lepton_line_object_set_y1(LeptonObject * object,gint y)276 lepton_line_object_set_y1 (LeptonObject *object,
277 gint y)
278 {
279 g_return_if_fail (object != NULL);
280 g_return_if_fail (object->line != NULL);
281
282 object->line->y[1] = y;
283 }
284
285 /*! \brief Modify the description of a line LeptonObject.
286 * \par Function Description
287 * This function modifies the coordinates of one of the two ends of
288 * the line described by <B>*object</B>. The new coordinates of this end,
289 * identified by <B>whichone</B>, are given by <B>x</B> and <B>y</B>
290 * in world unit.
291 *
292 * The coordinates of the end of line is modified in the world
293 * coordinate system. Screen coordinates and boundings are then updated.
294 *
295 * \param [in,out] object Line LeptonObject to modify.
296 * \param [in] x New x coordinate.
297 * \param [in] y New y coordinate.
298 * \param [in] whichone Which line parameter to modify.
299 *
300 * <B>whichone</B> can have the following values:
301 * <DL>
302 * <DT>*</DT><DD>LINE_END1
303 * <DT>*</DT><DD>LINE_END2
304 * </DL>
305 */
306 void
lepton_line_object_modify(LeptonObject * object,int x,int y,int whichone)307 lepton_line_object_modify (LeptonObject *object,
308 int x,
309 int y,
310 int whichone)
311 {
312 lepton_object_emit_pre_change_notify (object);
313
314 switch (whichone) {
315 case LINE_END1:
316 lepton_line_object_set_x0 (object, x);
317 lepton_line_object_set_y0 (object, y);
318 break;
319
320 case LINE_END2:
321 lepton_line_object_set_x1 (object, x);
322 lepton_line_object_set_y1 (object, y);
323 break;
324
325 default:
326 return;
327 }
328
329 lepton_object_emit_change_notify (object);
330 }
331
332 /*! \brief Create line LeptonObject from character string.
333 * \par Function Description
334 * This function creates a line LeptonObject from the character
335 * string <B>*buf</B> the description of a box.
336 *
337 * The function returns a pointer on the new last element, that is
338 * the added line object.
339 *
340 * Depending on <B>*version</B>, the correct file format is considered.
341 * Currently two file format revisions are supported :
342 * <DL>
343 * <DT>*</DT><DD>the file format used until 20010704 release.
344 * <DT>*</DT><DD>the file format used for the releases after 20010704.
345 * </DL>
346 *
347 * \param [in] buf Character string with line description.
348 * \param [in] release_ver libgeda release version number.
349 * \param [in] fileformat_ver libgeda file format version number.
350 * \return A pointer to the new line object, or NULL on error.
351 */
352 LeptonObject*
o_line_read(const char buf[],unsigned int release_ver,unsigned int fileformat_ver,GError ** err)353 o_line_read (const char buf[],
354 unsigned int release_ver,
355 unsigned int fileformat_ver,
356 GError ** err)
357 {
358 LeptonObject *new_obj;
359 char type;
360 int x1, y1;
361 int x2, y2;
362 int line_width, line_space, line_length;
363 int line_end;
364 int line_type;
365 int color;
366
367 if (release_ver <= VERSION_20000704) {
368 /*
369 * The old geda file format, i.e. releases 20000704 and older, does
370 * not handle the line type and the filling - here filling is irrelevant.
371 * They are set to default.
372 */
373 if (sscanf (buf, "%c %d %d %d %d %d\n", &type,
374 &x1, &y1, &x2, &y2, &color) != 6) {
375 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse line object"));
376 return NULL;
377 }
378
379 line_width = 0;
380 line_end = END_NONE;
381 line_type = TYPE_SOLID;
382 line_length= -1;
383 line_space = -1;
384 } else {
385 /*
386 * The current line format to describe a line is a space separated
387 * list of characters and numbers in plain ASCII on a single line.
388 * The meaning of each item is described in the file format documentation.
389 */
390 if (sscanf (buf, "%c %d %d %d %d %d %d %d %d %d %d\n", &type,
391 &x1, &y1, &x2, &y2, &color,
392 &line_width, &line_end, &line_type, &line_length, &line_space) != 11) {
393 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse line object"));
394 return NULL;
395 }
396 }
397
398 /*
399 * Null length line are not allowed. If such a line is detected a
400 * message is issued.
401 *
402 * It also checks is the required color is valid.
403 */
404 if (x1 == x2 && y1 == y2) {
405 g_message (_("Found a zero length line "
406 "[ %1$c %2$d %3$d %4$d %5$d %6$d ]"),
407 type, x1, y1, x2, y2, color);
408 }
409
410 if (!color_id_valid (color)) {
411 g_message (_("Found an invalid color [ %1$s ]"), buf);
412 g_message (_("Setting color to default color."));
413 color = default_color_id();
414 }
415
416 /*
417 * A line is internally described by its two ends. A new object is
418 * allocated, initialized and added to the list of objects. Its line
419 * type is set according to the values of the fields on the line.
420 */
421 /* create and add the line to the list */
422 new_obj = lepton_line_object_new (color,
423 x1,
424 y1,
425 x2,
426 y2);
427
428 /* set its line options */
429 lepton_object_set_line_options (new_obj,
430 (LeptonStrokeCapType) line_end,
431 (LeptonStrokeType) line_type,
432 line_width,
433 line_length,
434 line_space);
435 /* filling is irrelevant for line, just set to default */
436 lepton_object_set_fill_options (new_obj,
437 FILLING_HOLLOW,
438 -1,
439 -1,
440 -1,
441 -1,
442 -1);
443
444 return new_obj;
445 }
446
447 /*! \brief Create a character string representation of a line LeptonObject.
448 * \par Function Description
449 * The function formats a string in the buffer <B>*buff</B> to describe
450 * the box object <B>*object</B>.
451 * It follows the post-20000704 release file format that handle the
452 * line type and fill options - filling is irrelevant here.
453 *
454 * \param [in] object Line LeptonObject to create string from.
455 * \return A pointer to the line LeptonObject character string.
456 *
457 * \note
458 * Caller must g_free returned character string.
459 *
460 */
461 gchar*
lepton_line_object_to_buffer(const LeptonObject * object)462 lepton_line_object_to_buffer (const LeptonObject *object)
463 {
464 g_return_val_if_fail (lepton_object_is_line (object), NULL);
465 g_return_val_if_fail (object->line != NULL, NULL);
466
467 return g_strdup_printf ("%c %d %d %d %d %d %d %d %d %d %d",
468 lepton_object_get_type (object),
469 lepton_line_object_get_x0 (object),
470 lepton_line_object_get_y0 (object),
471 lepton_line_object_get_x1 (object),
472 lepton_line_object_get_y1 (object),
473 lepton_object_get_color (object),
474 lepton_object_get_stroke_width (object),
475 lepton_object_get_stroke_cap_type (object),
476 lepton_object_get_stroke_type (object),
477 lepton_object_get_stroke_dash_length (object),
478 lepton_object_get_stroke_space_length (object));
479 }
480
481 /*! \brief Translate a line position in WORLD coordinates by a delta.
482 * \par Function Description
483 * This function applies a translation of (<B>x1</B>,<B>y1</B>) to the line
484 * described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world unit.
485 *
486 * \param [in,out] object Line LeptonObject to translate.
487 * \param [in] dx x distance to move.
488 * \param [in] dy y distance to move.
489 */
490 void
lepton_line_object_translate(LeptonObject * object,int dx,int dy)491 lepton_line_object_translate (LeptonObject *object,
492 int dx,
493 int dy)
494 {
495 g_return_if_fail (lepton_object_is_line (object));
496 g_return_if_fail (object->line != NULL);
497
498 /* Update world coords */
499 object->line->x[0] = object->line->x[0] + dx;
500 object->line->y[0] = object->line->y[0] + dy;
501 object->line->x[1] = object->line->x[1] + dx;
502 object->line->y[1] = object->line->y[1] + dy;
503 }
504
505 /*! \brief Rotate Line LeptonObject using WORLD coordinates.
506 * \par Function Description
507 * This function rotates the line described by
508 * <B>*object</B> around the (<B>world_centerx</B>,<B>world_centery</B>)
509 * point by <B>angle</B> degrees.
510 * The center of rotation is in world units.
511 *
512 * \param [in] world_centerx Rotation center x coordinate in WORLD units.
513 * \param [in] world_centery Rotation center y coordinate in WORLD units.
514 * \param [in] angle Rotation angle in degrees (See note below).
515 * \param [in,out] object Line LeptonObject to rotate.
516 */
517 void
lepton_line_object_rotate(int world_centerx,int world_centery,int angle,LeptonObject * object)518 lepton_line_object_rotate (int world_centerx,
519 int world_centery,
520 int angle,
521 LeptonObject *object)
522 {
523 int newx, newy;
524
525 g_return_if_fail (lepton_object_is_line (object));
526 g_return_if_fail (object->line != NULL);
527
528 if (angle == 0)
529 return;
530
531 /* angle must be positive */
532 if(angle < 0) angle = -angle;
533 /* angle must be 90 multiple or no rotation performed */
534 if((angle % 90) != 0) return;
535
536 /*
537 * The center of rotation (<B>world_centerx</B>,<B>world_centery</B>)
538 * is translated to the origin. The rotation of the two ends of
539 * the line is performed. FInally, the rotated line is translated
540 * back to its previous location.
541 */
542 /* translate object to origin */
543 lepton_line_object_translate (object, -world_centerx, -world_centery);
544
545 /* rotate line end 1 */
546 lepton_point_rotate_90 (object->line->x[0],
547 object->line->y[0],
548 angle,
549 &newx,
550 &newy);
551
552 object->line->x[0] = newx;
553 object->line->y[0] = newy;
554
555 /* rotate line end 2 */
556 lepton_point_rotate_90 (object->line->x[1],
557 object->line->y[1],
558 angle,
559 &newx,
560 &newy);
561
562 object->line->x[1] = newx;
563 object->line->y[1] = newy;
564
565 /* translate object back to normal position */
566 lepton_line_object_translate (object, world_centerx, world_centery);
567
568 }
569
570 /*! \brief Mirror a line using WORLD coordinates.
571 * \par Function Description
572 * This function mirrors the line from the point
573 * (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
574 *
575 * The line if first translated to the origin, then mirrored
576 * and finally translated back at its previous position.
577 *
578 * \param [in] world_centerx Origin x coordinate in WORLD units.
579 * \param [in] world_centery Origin y coordinate in WORLD units.
580 * \param [in,out] object Line LeptonObject to mirror.
581 */
582 void
lepton_line_object_mirror(int world_centerx,int world_centery,LeptonObject * object)583 lepton_line_object_mirror (int world_centerx,
584 int world_centery,
585 LeptonObject *object)
586 {
587 g_return_if_fail (lepton_object_is_line (object));
588 g_return_if_fail (object->line != NULL);
589
590 /* translate object to origin */
591 lepton_line_object_translate (object, -world_centerx, -world_centery);
592
593 /* mirror the line ends */
594 object->line->x[0] = -object->line->x[0];
595 object->line->x[1] = -object->line->x[1];
596
597 /* translate back in position */
598 lepton_line_object_translate (object, world_centerx, world_centery);
599
600 }
601
602 /*! \brief Calculate the bounds of the line
603 *
604 * On failure, this function sets the bounds to empty.
605 *
606 * \param [in] object The line object
607 * \param [out] bounds The bounds of the line
608 */
609 void
lepton_line_object_calculate_bounds(const LeptonObject * object,LeptonBounds * bounds)610 lepton_line_object_calculate_bounds (const LeptonObject *object,
611 LeptonBounds *bounds)
612 {
613 gint expand;
614
615 lepton_bounds_init (bounds);
616
617 g_return_if_fail (lepton_object_is_line (object));
618 g_return_if_fail (object->line != NULL);
619
620 lepton_line_calculate_bounds (object->line, bounds);
621
622 expand = ceil (0.5 * G_SQRT2 * lepton_object_get_stroke_width (object));
623
624 /* This isn't strictly correct, but a 1st order approximation */
625 lepton_bounds_expand (bounds, bounds, expand, expand);
626 }
627
628 /*! \brief get the position of the first line point
629 * \par Function Description
630 * This function gets the position of the first point of a line object.
631 *
632 * \param [in] object The object to get the position.
633 * \param [out] x pointer to the x-position
634 * \param [out] y pointer to the y-position
635 * \return TRUE if successfully determined the position, FALSE otherwise
636 */
637 gboolean
lepton_line_object_get_position(const LeptonObject * object,gint * x,gint * y)638 lepton_line_object_get_position (const LeptonObject *object,
639 gint *x,
640 gint *y)
641 {
642 g_return_val_if_fail (lepton_object_is_line (object), FALSE);
643 g_return_val_if_fail (object->line != NULL, FALSE);
644
645 if (x != NULL) {
646 *x = object->line->x[0];
647 }
648
649 if (y != NULL) {
650 *y = object->line->y[0];
651 }
652
653 return TRUE;
654 }
655
656 /*! \brief calculate the length of a line object
657 * \par Function Description
658 * This function calculates the length of a line object
659 *
660 * \param [in] object a line LeptonObject
661 * \return The length of the line
662 */
663 double
lepton_line_object_length(LeptonObject * object)664 lepton_line_object_length (LeptonObject *object)
665 {
666 double length;
667 double dx, dy;
668
669 if (!object->line) {
670 return 0.0;
671 }
672
673 dx = object->line->x[0]-object->line->x[1];
674 dy = object->line->y[0]-object->line->y[1];
675
676 length = hypot(dx, dy);
677
678 return(length);
679 }
680
681 /*! \brief Calculates the distance between the given point and the closest
682 * point on the given line segment.
683 *
684 * If the closest point on the line resides beyond the line segment's
685 * end point, this function returns the distance from the given point to the
686 * closest end point.
687 *
688 * If the line represents a single point (the endpoints are the same), this
689 * function calcualtes the distance to that point.
690 *
691 * \param [in] object The line LeptonObject.
692 * \param [in] x The x coordinate of the given point.
693 * \param [in] y The y coordinate of the given point.
694 * \param [in] force_solid If true, force treating the object as solid.
695 * \param [in] include_hidden Take hidden text into account.
696 * \return The shortest distance from the object to the point. With an
697 * invalid parameter, this function returns G_MAXDOUBLE.
698 */
699 double
lepton_line_object_shortest_distance(LeptonObject * object,int x,int y,int force_solid,gboolean include_hidden)700 lepton_line_object_shortest_distance (LeptonObject *object,
701 int x,
702 int y,
703 int force_solid,
704 gboolean include_hidden)
705 {
706 return lepton_line_shortest_distance (object->line, x, y);
707 }
708