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 lepton_bus_object.c
22  *
23  *  \brief Functions operating on bus objects
24  */
25 
26 #include <config.h>
27 
28 #ifdef HAVE_MATH_H
29 #include <math.h>
30 #endif
31 
32 #include "liblepton_priv.h"
33 
34 /*! \brief Get the ripper direction
35  *
36  *  \param [in] object The bus object
37  *  \return The ripper direction
38  */
39 gint
lepton_bus_object_get_ripper_direction(const LeptonObject * object)40 lepton_bus_object_get_ripper_direction (const LeptonObject *object)
41 {
42   g_return_val_if_fail (lepton_object_is_bus (object), 0);
43   g_return_val_if_fail (object->bus_ripper_direction >= -1, -1);
44   g_return_val_if_fail (object->bus_ripper_direction <= 1, 1);
45 
46   return object->bus_ripper_direction;
47 }
48 
49 /*! \brief Get the x coordinate of first endpoint
50  *
51  *  The coordinate properties are broken out individually to make it easier for
52  *  the GUI. This way, the GUI does not need as many adapters to interface to
53  *  a line boxed type.
54  *
55  *  \param [in] object The line
56  *  \return The x coordinate for the first endpoint
57  */
58 gint
lepton_bus_object_get_x0(const LeptonObject * object)59 lepton_bus_object_get_x0 (const LeptonObject *object)
60 {
61   g_return_val_if_fail (lepton_object_is_bus (object), 0);
62   g_return_val_if_fail (object->line != NULL, 0);
63 
64   return object->line->x[0];
65 }
66 
67 /*! \brief Get the x coordinate of second endpoint
68  *
69  *  The coordinate properties are broken out individually to make it easier for
70  *  the GUI. This way, the GUI does not need as many adapters to interface to
71  *  a line boxed type.
72  *
73  *  \param [in] object The line
74  *  \return The x coordinate for the second endpoint
75  */
76 gint
lepton_bus_object_get_x1(const LeptonObject * object)77 lepton_bus_object_get_x1 (const LeptonObject *object)
78 {
79   g_return_val_if_fail (lepton_object_is_bus (object), 0);
80   g_return_val_if_fail (object->line != NULL, 0);
81 
82   return object->line->x[1];
83 }
84 
85 /*! \brief Get the y coordinate of first endpoint
86  *
87  *  The coordinate properties are broken out individually to make it easier for
88  *  the GUI. This way, the GUI does not need as many adapters to interface to
89  *  a line boxed type.
90  *
91  *  \param [in] object The line
92  *  \return The y coordinate for the first endpoint
93  */
94 gint
lepton_bus_object_get_y0(const LeptonObject * object)95 lepton_bus_object_get_y0 (const LeptonObject *object)
96 {
97   g_return_val_if_fail (lepton_object_is_bus (object), 0);
98   g_return_val_if_fail (object->line != NULL, 0);
99 
100   return object->line->y[0];
101 }
102 
103 /*! \brief Get the y coordinate of second endpoint
104  *
105  *  The coordinate properties are broken out individually to make it easier for
106  *  the GUI. This way, the GUI does not need as many adapters to interface to
107  *  a line boxed type.
108  *
109  *  \param [in] object The line
110  *  \return The y coordinate for the second endpoint
111  */
112 gint
lepton_bus_object_get_y1(const LeptonObject * object)113 lepton_bus_object_get_y1 (const LeptonObject *object)
114 {
115   g_return_val_if_fail (lepton_object_is_bus (object), 0);
116   g_return_val_if_fail (object->line != NULL, 0);
117 
118   return object->line->y[1];
119 }
120 
121 /*! \brief Set the ripper direction
122  *
123  *  \param [in,out] object The bus object
124  *  \param [in] direction The ripper direction
125  */
126 void
lepton_bus_object_set_ripper_direction(LeptonObject * object,gint direction)127 lepton_bus_object_set_ripper_direction (LeptonObject *object,
128                                         gint direction)
129 {
130   g_return_if_fail (lepton_object_is_bus (object));
131   g_return_if_fail (direction >= -1);
132   g_return_if_fail (direction <= 1);
133 
134   object->bus_ripper_direction = direction;
135 }
136 
137 /*! \brief Set 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,out] object The line
144  *  \param [in] x The new x coordinate for the first endpoint
145  */
146 void
lepton_bus_object_set_x0(LeptonObject * object,gint x)147 lepton_bus_object_set_x0 (LeptonObject *object,
148                           gint x)
149 {
150   g_return_if_fail (lepton_object_is_bus (object));
151   g_return_if_fail (object->line != NULL);
152 
153   object->line->x[0] = x;
154 }
155 
156 /*! \brief Set the x coordinate of second endpoint
157  *
158  *  The coordinate properties are broken out individually to make it easier for
159  *  the GUI. This way, the GUI does not need as many adapters to interface to
160  *  a line boxed type.
161  *
162  *  \param [in,out] object The line
163  *  \param [in] x The new x coordinate for the second endpoint
164  */
165 void
lepton_bus_object_set_x1(LeptonObject * object,gint x)166 lepton_bus_object_set_x1 (LeptonObject *object,
167                           gint x)
168 {
169   g_return_if_fail (lepton_object_is_bus (object));
170   g_return_if_fail (object->line != NULL);
171 
172   object->line->x[1] = x;
173 }
174 
175 /*! \brief Set the y coordinate of first endpoint
176  *
177  *  The coordinate properties are broken out individually to make it easier for
178  *  the GUI. This way, the GUI does not need as many adapters to interface to
179  *  a line boxed type.
180  *
181  *  \param [in,out] object The line
182  *  \param [in] y The new y coordinate for the first endpoint
183  */
184 void
lepton_bus_object_set_y0(LeptonObject * object,gint y)185 lepton_bus_object_set_y0 (LeptonObject *object,
186                           gint y)
187 {
188   g_return_if_fail (lepton_object_is_bus (object));
189   g_return_if_fail (object->line != NULL);
190 
191   object->line->y[0] = y;
192 }
193 
194 /*! \brief Set the y coordinate of second endpoint
195  *
196  *  The coordinate properties are broken out individually to make it easier for
197  *  the GUI. This way, the GUI does not need as many adapters to interface to
198  *  a line boxed type.
199  *
200  *  \param [in,out] object The line
201  *  \param [in] y The new y coordinate for the second endpoint
202  */
203 void
lepton_bus_object_set_y1(LeptonObject * object,gint y)204 lepton_bus_object_set_y1 (LeptonObject *object,
205                           gint y)
206 {
207   g_return_if_fail (lepton_object_is_bus (object));
208   g_return_if_fail (object->line != NULL);
209 
210   object->line->y[1] = y;
211 }
212 
213 /*! \brief get the position of the first bus point
214  *  \par Function Description
215  *  This function gets the position of the first point of a bus object.
216  *
217  *  \param [in] object   The object to get the position.
218  *  \param [out] x       pointer to the x-position
219  *  \param [out] y       pointer to the y-position
220  *  \return TRUE if successfully determined the position, FALSE otherwise
221  */
222 gboolean
lepton_bus_object_get_position(const LeptonObject * object,gint * x,gint * y)223 lepton_bus_object_get_position (const LeptonObject *object,
224                                 gint *x,
225                                 gint *y)
226 {
227   g_return_val_if_fail (lepton_object_is_bus (object), FALSE);
228   g_return_val_if_fail (object->line != NULL, FALSE);
229 
230   if (x != NULL) {
231     *x = object->line->x[0];
232   }
233 
234   if (y != NULL) {
235     *y = object->line->y[0];
236   }
237 
238   return TRUE;
239 }
240 
241 /*! \brief Calculate the bounds of the bus
242  *
243  *  On failure, this function sets the bounds to empty.
244  *
245  *  \param [in]  object   The bus object
246  *  \param [out] bounds   The bounds of the bus
247  */
248 void
lepton_bus_object_calculate_bounds(const LeptonObject * object,LeptonBounds * bounds)249 lepton_bus_object_calculate_bounds (const LeptonObject *object,
250                                     LeptonBounds *bounds)
251 {
252   gint expand;
253 
254   lepton_bounds_init (bounds);
255 
256   g_return_if_fail (lepton_object_is_bus (object));
257   g_return_if_fail (object->line != NULL);
258 
259   lepton_line_calculate_bounds (object->line, bounds);
260 
261   expand = ceil (0.5 * G_SQRT2 * BUS_WIDTH);
262 
263   /* This isn't strictly correct, but a 1st order approximation */
264   lepton_bounds_expand (bounds, bounds, expand, expand);
265 }
266 
267 /*! \brief create a new bus object
268  *  \par Function Description
269  *  This function creates and returns a new bus object.
270  *
271  *  \param [in]     color       The color of the bus
272  *  \param [in]     x1          x-coord of the first point
273  *  \param [in]     y1          y-coord of the first point
274  *  \param [in]     x2          x-coord of the second point
275  *  \param [in]     y2          y-coord of the second point
276  *  \param [in]  bus_ripper_direction direction of the bus rippers
277  *  \return A new bus LeptonObject
278  */
279 LeptonObject*
lepton_bus_object_new(gint color,gint x1,gint y1,gint x2,gint y2,gint bus_ripper_direction)280 lepton_bus_object_new (gint color,
281                        gint x1,
282                        gint y1,
283                        gint x2,
284                        gint y2,
285                        gint bus_ripper_direction)
286 {
287   LeptonObject *new_node;
288 
289   new_node = lepton_object_new (OBJ_BUS, "bus");
290   lepton_object_set_color (new_node, color);
291 
292   new_node->line = lepton_line_new ();
293   /* check for null */
294 
295   new_node->line->x[0] = x1;
296   new_node->line->y[0] = y1;
297   new_node->line->x[1] = x2;
298   new_node->line->y[1] = y2;
299   lepton_object_set_stroke_width (new_node, BUS_WIDTH);
300 
301   new_node->bus_ripper_direction = bus_ripper_direction;
302 
303   return new_node;
304 }
305 
306 /*! \brief read a bus object from a char buffer
307  *  \par Function Description
308  *  This function reads a bus object from the buffer \a buf.
309  *  If the bus object was read successfully, a new bus object is
310  *  allocated and appended to the \a object_list.
311  *
312  *  \param [in] buf          a text buffer (usually a line of a schematic file)
313  *  \param [in] release_ver  The release number gEDA
314  *  \param [in] fileformat_ver a integer value of the file format
315  *  \return The object list, or NULL on error.
316  */
317 LeptonObject*
o_bus_read(const char buf[],unsigned int release_ver,unsigned int fileformat_ver,GError ** err)318 o_bus_read (const char buf[],
319             unsigned int release_ver,
320             unsigned int fileformat_ver,
321             GError **err)
322 {
323   LeptonObject *new_obj;
324   char type;
325   int x1, y1;
326   int x2, y2;
327   int color;
328   int ripper_dir;
329 
330   if (release_ver <= VERSION_20020825) {
331     if (sscanf (buf, "%c %d %d %d %d %d\n", &type, &x1, &y1, &x2, &y2, &color) != 6) {
332       g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse bus object"));
333       return NULL;
334     }
335     ripper_dir = 0;
336   } else {
337     if (sscanf (buf, "%c %d %d %d %d %d %d\n", &type, &x1, &y1, &x2, &y2, &color,
338                 &ripper_dir) != 7) {
339       g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse bus object"));
340       return NULL;
341     }
342   }
343 
344   if (x1 == x2 && y1 == y2) {
345     g_message (_("Found a zero length bus "
346                  "[ %1$c %2$d %3$d %4$d %5$d %6$d ]"),
347                type, x1, y1, x2, y2, color);
348   }
349 
350   if (!color_id_valid (color)) {
351     g_message (_("Found an invalid color [ %1$s ]"), buf);
352     g_message (_("Setting color to default color."));
353     color = default_color_id();
354   }
355 
356   if (ripper_dir < -1 || ripper_dir > 1) {
357     g_message (_("Found an invalid bus ripper direction [ %1$s ]"), buf);
358     g_message (_("Resetting direction to neutral (no direction)."));
359     ripper_dir = 0;
360   }
361 
362   new_obj = lepton_bus_object_new (color, x1, y1, x2, y2, ripper_dir);
363 
364   return new_obj;
365 }
366 
367 /*! \brief Create a string representation of the bus object
368  *  \par Function Description
369  *  This function takes a bus \a object and return a string
370  *  according to the file format definition.
371  *
372  *  \param [in] object  a bus LeptonObject
373  *  \return the string representation of the bus LeptonObject
374  */
375 gchar*
lepton_bus_object_to_buffer(const LeptonObject * object)376 lepton_bus_object_to_buffer (const LeptonObject *object)
377 {
378   g_return_val_if_fail (lepton_object_is_bus (object), NULL);
379   g_return_val_if_fail (object->line != NULL, NULL);
380 
381   return g_strdup_printf ("%c %d %d %d %d %d %d",
382                           lepton_object_get_type (object),
383                           lepton_bus_object_get_x0 (object),
384                           lepton_bus_object_get_y0 (object),
385                           lepton_bus_object_get_x1 (object),
386                           lepton_bus_object_get_y1 (object),
387                           lepton_object_get_color (object),
388                           lepton_bus_object_get_ripper_direction (object));
389 }
390 
391 /*! \brief move a bus object
392  *  \par Function Description
393  *  This function changes the position of a bus \a object.
394  *
395  *  \param [in,out] object The bus LeptonObject to be moved
396  *  \param [in]     dx     The x-distance to move the object
397  *  \param [in]     dy     The y-distance to move the object
398  */
399 void
lepton_bus_object_translate(LeptonObject * object,gint dx,gint dy)400 lepton_bus_object_translate (LeptonObject *object,
401                              gint dx,
402                              gint dy)
403 {
404   g_return_if_fail (lepton_object_is_bus (object));
405   g_return_if_fail (object->line != NULL);
406 
407   /* Update world coords */
408   object->line->x[0] = object->line->x[0] + dx;
409   object->line->y[0] = object->line->y[0] + dy;
410   object->line->x[1] = object->line->x[1] + dx;
411   object->line->y[1] = object->line->y[1] + dy;
412 }
413 
414 /*! \brief create a copy of a bus object
415  *  \par Function Description
416  *  This function creates a copy of the bus object \a o_current.
417  *
418  *  \param [in] o_current    The object that is copied
419  *  \return a new bus object
420  */
421 LeptonObject*
lepton_bus_object_copy(const LeptonObject * object)422 lepton_bus_object_copy (const LeptonObject *object)
423 {
424   LeptonObject *new_obj;
425 
426   g_return_val_if_fail (lepton_object_is_bus (object), NULL);
427   g_return_val_if_fail (object->line != NULL, NULL);
428 
429   /* make sure you fix this in pin and bus as well */
430   /* still doesn't work... you need to pass in the new values */
431   /* or don't update and update later */
432   /* I think for now I'll disable the update and manually update */
433   new_obj = lepton_bus_object_new (lepton_object_get_color (object),
434                                    object->line->x[0],
435                                    object->line->y[0],
436                                    object->line->x[1],
437                                    object->line->y[1],
438                                    object->bus_ripper_direction);
439 
440   return new_obj;
441 }
442 
443 /*! \brief rotate a bus object around a centerpoint
444  *  \par Function Description
445  *  This function rotates a bus \a object around the point
446  *  (\a world_centerx, \a world_centery).
447  *
448  *  \param [in]     world_centerx x-coord of the rotation center
449  *  \param [in]     world_centery y-coord of the rotation center
450  *  \param [in]     angle         The angle to rotate the bus object
451  *  \param [in,out] object        The bus object
452  *  \note only steps of 90 degrees are allowed for the \a angle
453  */
454 void
lepton_bus_object_rotate(gint world_centerx,gint world_centery,gint angle,LeptonObject * object)455 lepton_bus_object_rotate (gint world_centerx,
456                           gint world_centery,
457                           gint angle,
458                           LeptonObject *object)
459 {
460   gint newx, newy;
461 
462   g_return_if_fail (lepton_object_is_bus (object));
463   g_return_if_fail (object->line != NULL);
464   g_return_if_fail (lepton_angle_is_ortho (angle));
465 
466   if (angle == 0) {
467     return;
468   }
469 
470   /* translate object to origin */
471   lepton_bus_object_translate (object, -world_centerx, -world_centery);
472 
473   lepton_point_rotate_90 (object->line->x[0],
474                           object->line->y[0],
475                           angle,
476                           &newx,
477                           &newy);
478 
479   object->line->x[0] = newx;
480   object->line->y[0] = newy;
481 
482   lepton_point_rotate_90 (object->line->x[1],
483                           object->line->y[1],
484                           angle,
485                           &newx,
486                           &newy);
487 
488   object->line->x[1] = newx;
489   object->line->y[1] = newy;
490 
491   lepton_bus_object_translate (object, world_centerx, world_centery);
492 }
493 
494 /*! \brief mirror a bus object horizontaly at a centerpoint
495  *  \par Function Description
496  *  This function mirrors a bus \a object horizontaly at the point
497  *  (\a world_centerx, \a world_centery).
498  *
499  *  \param [in]     world_centerx x-coord of the mirror position
500  *  \param [in]     world_centery y-coord of the mirror position
501  *  \param [in,out] object        The bus object
502  */
503 void
lepton_bus_object_mirror(gint world_centerx,gint world_centery,LeptonObject * object)504 lepton_bus_object_mirror (gint world_centerx,
505                           gint world_centery,
506                           LeptonObject *object)
507 {
508   g_return_if_fail (lepton_object_is_bus (object));
509   g_return_if_fail (object->line != NULL);
510 
511   /* translate object to origin */
512   lepton_bus_object_translate (object, -world_centerx, -world_centery);
513 
514   object->line->x[0] = -object->line->x[0];
515 
516   object->line->x[1] = -object->line->x[1];
517 
518   lepton_bus_object_translate (object, world_centerx, world_centery);
519 }
520 
521 /*! \brief calculate the orientation of a bus object
522  *  \par Function Description
523  *  This function calculates the orientation of a bus object.
524  *
525  *  \param [in] object   The bus object
526  *  \return The orientation: HORIZONTAL, VERTICAL or NEITHER
527  */
528 gint
lepton_bus_object_orientation(const LeptonObject * object)529 lepton_bus_object_orientation (const LeptonObject *object)
530 {
531   g_return_val_if_fail (lepton_object_is_bus (object), NEITHER);
532   g_return_val_if_fail (object->line != NULL, NEITHER);
533 
534   if (object->line->y[0] == object->line->y[1]) {
535     return(HORIZONTAL);
536   }
537 
538   if (object->line->x[0] == object->line->x[1]) {
539     return(VERTICAL);
540   }
541 
542   return(NEITHER);
543 }
544 
545 /*! \brief modify one point of a bus object
546  *  \par Function Description
547  *  This function modifies one point of a bus \a object. The point
548  *  is specified by the \a whichone variable and the new coordinate
549  *  is (\a x, \a y).
550  *
551  *  \param [in,out] object     The bus LeptonObject to modify
552  *  \param [in]     x          new x-coord of the bus point
553  *  \param [in]     y          new y-coord of the bus point
554  *  \param [in]     whichone   bus point to modify
555  */
556 void
lepton_bus_object_modify(LeptonObject * object,gint x,gint y,gint whichone)557 lepton_bus_object_modify (LeptonObject *object,
558                           gint x,
559                           gint y,
560                           gint whichone)
561 {
562   g_return_if_fail (lepton_object_is_bus (object));
563   g_return_if_fail (object->line != NULL);
564   g_return_if_fail (whichone >= LINE_END1);
565   g_return_if_fail (whichone <= LINE_END2);
566 
567   object->line->x[whichone] = x;
568   object->line->y[whichone] = y;
569 }
570