1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
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.
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.
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
25 #include "libgeda_priv.h"
27 #ifdef HAVE_LIBDMALLOC
31 /*! \file o_net_basic.c
32 * \brief functions for the net object
35 /*! \brief get the position of the first net point
36 * \par Function Description
37 * This function gets the position of the first point of a net object.
39 * \param [in] toplevel The toplevel environment.
40 * \param [out] x pointer to the x-position
41 * \param [out] y pointer to the y-position
42 * \param [in] object The object to get the position.
43 * \return TRUE if successfully determined the position, FALSE otherwise
45 gboolean
o_net_get_position (TOPLEVEL
*toplevel
, gint
*x
, gint
*y
,
48 return o_line_get_position(toplevel
, x
, y
, object
);
51 /*! \brief calculate and return the boundaries of a net object
52 * \par Function Description
53 * This function calculates the object boudaries of a net \a object.
55 * \param [in] toplevel The TOPLEVEL object.
56 * \param [in] object a net object
57 * \param [out] left the left world coord
58 * \param [out] top the top world coord
59 * \param [out] right the right world coord
60 * \param [out] bottom the bottom world coord
62 void world_get_net_bounds(TOPLEVEL
*toplevel
, OBJECT
*object
, int *left
,
63 int *top
, int *right
, int *bottom
)
65 world_get_line_bounds( toplevel
, object
, left
, top
, right
, bottom
);
68 /*! \brief create a new net object
69 * \par Function Description
70 * This function creates and returns a new net object.
72 * \param [in] toplevel The TOPLEVEL object.
73 * \param [in] type The OBJECT type (usually OBJ_NET)
74 * \param [in] color The color of the net
75 * \param [in] x1 x-coord of the first point
76 * \param [in] y1 y-coord of the first point
77 * \param [in] x2 x-coord of the second point
78 * \param [in] y2 y-coord of the second point
79 * \return A new net OBJECT
81 OBJECT
*o_net_new(TOPLEVEL
*toplevel
, char type
,
82 int color
, int x1
, int y1
, int x2
, int y2
)
86 new_node
= s_basic_new_object(type
, "net");
87 new_node
->color
= color
;
89 new_node
->line
= (LINE
*) g_malloc(sizeof(LINE
));
92 new_node
->line
->x
[0] = x1
;
93 new_node
->line
->y
[0] = y1
;
94 new_node
->line
->x
[1] = x2
;
95 new_node
->line
->y
[1] = y2
;
96 new_node
->line_width
= NET_WIDTH
;
98 o_net_recalc (toplevel
, new_node
);
103 /*! \brief recalc the visual properties of a net object
104 * \par Function Description
105 * This function updates the visual coords of the \a o_current object.
107 * \param [in] toplevel The TOPLEVEL object.
108 * \param [in] o_current a net object.
111 void o_net_recalc(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
113 int left
, right
, top
, bottom
;
115 if (o_current
== NULL
) {
119 if (o_current
->line
== NULL
) {
123 world_get_net_bounds(toplevel
, o_current
, &left
, &top
, &right
,
126 o_current
->w_left
= left
;
127 o_current
->w_top
= top
;
128 o_current
->w_right
= right
;
129 o_current
->w_bottom
= bottom
;
130 o_current
->w_bounds_valid
= TRUE
;
133 /*! \brief read a net object from a char buffer
134 * \par Function Description
135 * This function reads a net object from the buffer \a buf.
136 * If the netobject was read successfully, a new net object is
137 * allocated and appended to the \a object_list.
139 * \param [in] toplevel The TOPLEVEL object
140 * \param [in] buf a text buffer (usually a line of a schematic file)
141 * \param [in] release_ver The release number gEDA
142 * \param [in] fileformat_ver a integer value of the file format
143 * \return The object list, or NULL on error.
146 OBJECT
*o_net_read (TOPLEVEL
*toplevel
, const char buf
[],
147 unsigned int release_ver
, unsigned int fileformat_ver
, GError
**err
)
155 if (sscanf (buf
, "%c %d %d %d %d %d\n", &type
, &x1
, &y1
, &x2
, &y2
, &color
) != 6) {
156 g_set_error(err
, EDA_ERROR
, EDA_ERROR_PARSE
, _("Failed to parse net object"));
160 if (x1
== x2
&& y1
== y2
) {
161 s_log_message (_("Found a zero length net [ %c %d %d %d %d %d ]\n"),
162 type
, x1
, y1
, x2
, y2
, color
);
166 if (toplevel
->override_net_color
!= -1) {
167 color
= toplevel
->override_net_color
;
170 if (color
< 0 || color
> MAX_COLORS
) {
171 s_log_message (_("Found an invalid color [ %s ]\n"), buf
);
172 s_log_message (_("Setting color to default color\n"));
173 color
= DEFAULT_COLOR
;
176 new_obj
= o_net_new (toplevel
, type
, color
, x1
, y1
, x2
, y2
);
181 /*! \brief Create a string representation of the net object
182 * \par Function Description
183 * This function takes a net \a object and return a string
184 * according to the file format definition.
186 * \param [in] toplevel a TOPLEVEL structure
187 * \param [in] object a net OBJECT
188 * \return the string representation of the net OBJECT
190 char *o_net_save(TOPLEVEL
*toplevel
, OBJECT
*object
)
195 x1
= object
->line
->x
[0];
196 y1
= object
->line
->y
[0];
197 x2
= object
->line
->x
[1];
198 y2
= object
->line
->y
[1];
200 buf
= g_strdup_printf("%c %d %d %d %d %d", object
->type
, x1
, y1
, x2
, y2
, object
->color
);
204 /*! \brief move a net object
205 * \par Function Description
206 * This function changes the position of a net \a object.
208 * \param [in] toplevel The TOPLEVEL object
209 * \param [in] dx The x-distance to move the object
210 * \param [in] dy The y-distance to move the object
211 * \param [in] object The net OBJECT to be moved
214 void o_net_translate_world(TOPLEVEL
*toplevel
, int dx
, int dy
,
220 /* Update world coords */
221 object
->line
->x
[0] = object
->line
->x
[0] + dx
;
222 object
->line
->y
[0] = object
->line
->y
[0] + dy
;
223 object
->line
->x
[1] = object
->line
->x
[1] + dx
;
224 object
->line
->y
[1] = object
->line
->y
[1] + dy
;
226 /* Update bounding box */
227 o_net_recalc (toplevel
, object
);
229 s_tile_update_object(toplevel
, object
);
232 /*! \brief create a copy of a net object
233 * \par Function Description
234 * This function creates a copy of the net object \a o_current.
236 * \param [in] toplevel The TOPLEVEL object
237 * \param [in] o_current The object that is copied
238 * \return a new net object
240 OBJECT
*o_net_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
244 /* make sure you fix this in pin and bus as well */
245 /* still doesn't work... you need to pass in the new values */
246 /* or don't update and update later */
247 /* I think for now I'll disable the update and manually update */
248 new_obj
= o_net_new (toplevel
, OBJ_NET
, o_current
->color
,
249 o_current
->line
->x
[0], o_current
->line
->y
[0],
250 o_current
->line
->x
[1], o_current
->line
->y
[1]);
255 /*! \brief postscript print command for a net object
256 * \par Function Description
257 * This function writes the postscript command of the net object \a o_current
258 * into the FILE \a fp points to.
260 * \param [in] toplevel The TOPLEVEL object
261 * \param [in] fp pointer to a FILE structure
262 * \param [in] o_current The OBJECT to print
263 * \param [in] origin_x x-coord of the postscript origin
264 * \param [in] origin_y y-coord of the postscript origin
266 void o_net_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
267 int origin_x
, int origin_y
)
273 if (o_current
== NULL
) {
274 printf("got null in o_net_print\n");
278 f_print_set_color(toplevel
, fp
, o_current
->color
);
281 if (toplevel
->net_style
== THICK
) {
282 net_width
= NET_WIDTH
;
285 x1
= o_current
->line
->x
[0] - origin_x
,
286 y1
= o_current
->line
->y
[0] - origin_y
;
287 x2
= o_current
->line
->x
[1] - origin_x
,
288 y2
= o_current
->line
->y
[1] - origin_y
;
290 fprintf(fp
, "%d %d %d %d %d line\n", x1
,y1
,x2
,y2
,net_width
);
294 /*! \brief rotate a net object around a centerpoint
295 * \par Function Description
296 * This function rotates a net \a object around the point
297 * (\a world_centerx, \a world_centery).
299 * \param [in] toplevel The TOPLEVEL object
300 * \param [in] world_centerx x-coord of the rotation center
301 * \param [in] world_centery y-coord of the rotation center
302 * \param [in] angle The angle to rotat the net object
303 * \param [in] object The net object
304 * \note only steps of 90 degrees are allowed for the \a angle
306 void o_net_rotate_world(TOPLEVEL
*toplevel
,
307 int world_centerx
, int world_centery
, int angle
,
315 /* translate object to origin */
316 o_net_translate_world(toplevel
, -world_centerx
, -world_centery
,
319 rotate_point_90(object
->line
->x
[0], object
->line
->y
[0], angle
,
322 object
->line
->x
[0] = newx
;
323 object
->line
->y
[0] = newy
;
325 rotate_point_90(object
->line
->x
[1], object
->line
->y
[1], angle
,
328 object
->line
->x
[1] = newx
;
329 object
->line
->y
[1] = newy
;
331 o_net_translate_world(toplevel
, world_centerx
, world_centery
, object
);
334 /*! \brief mirror a net object horizontaly at a centerpoint
335 * \par Function Description
336 * This function mirrors a net \a object horizontaly at the point
337 * (\a world_centerx, \a world_centery).
339 * \param [in] toplevel The TOPLEVEL object
340 * \param [in] world_centerx x-coord of the mirror position
341 * \param [in] world_centery y-coord of the mirror position
342 * \param [in] object The net object
344 void o_net_mirror_world(TOPLEVEL
*toplevel
, int world_centerx
,
345 int world_centery
, OBJECT
*object
)
347 /* translate object to origin */
348 o_net_translate_world(toplevel
, -world_centerx
, -world_centery
,
351 object
->line
->x
[0] = -object
->line
->x
[0];
353 object
->line
->x
[1] = -object
->line
->x
[1];
355 o_net_translate_world(toplevel
, world_centerx
, world_centery
, object
);
358 /*! \brief calculate the orientation of a net object
359 * \par Function Description
360 * This function calculates the orientation of a net object.
362 * \param [in] object The net object
363 * \return The orientation: HORIZONTAL, VERTICAL or NEITHER
365 int o_net_orientation(OBJECT
*object
)
367 if (object
->line
->y
[0] == object
->line
->y
[1]) {
371 if (object
->line
->x
[0] == object
->line
->x
[1]) {
379 /*! \brief merge two net object
380 * \par Function Description
381 * This function does the actual work of making one net segment out of two
382 * connected segments. The first net segment is extended to the lenght of
384 * The second object (\a del_object) is the object that should be deleted.
386 * \param [in] object A net object to extend
387 * \param [in] del_object A net object to be merged into \a object
388 * \param [in] orient The orientation of both net objects
390 * \note The first net \a object gets the attributes of the second net
391 * \a del_object if the two nets are merged together.
393 static void o_net_consolidate_lowlevel (OBJECT
*object
,
394 OBJECT
*del_object
, int orient
)
403 printf("o %d %d %d %d\n", object
->line
->x
[0], object
->line
->y
[0],
404 object
->line
->x
[1], object
->line
->y
[1]);
405 printf("d %d %d %d %d\n", del_object
->line
->x
[0],
406 del_object
->line
->y
[0], del_object
->line
->x
[1],
407 del_object
->line
->y
[1]);
411 if (orient
== HORIZONTAL
) {
413 temp1
= min(object
->line
->x
[0], del_object
->line
->x
[0]);
414 temp2
= min(object
->line
->x
[1], del_object
->line
->x
[1]);
416 final1
= min(temp1
, temp2
);
418 temp1
= max(object
->line
->x
[0], del_object
->line
->x
[0]);
419 temp2
= max(object
->line
->x
[1], del_object
->line
->x
[1]);
421 final2
= max(temp1
, temp2
);
423 object
->line
->x
[0] = final1
;
424 object
->line
->x
[1] = final2
;
428 if (orient
== VERTICAL
) {
429 temp1
= min(object
->line
->y
[0], del_object
->line
->y
[0]);
430 temp2
= min(object
->line
->y
[1], del_object
->line
->y
[1]);
432 final1
= min(temp1
, temp2
);
434 temp1
= max(object
->line
->y
[0], del_object
->line
->y
[0]);
435 temp2
= max(object
->line
->y
[1], del_object
->line
->y
[1]);
437 final2
= max(temp1
, temp2
);
439 object
->line
->y
[0] = final1
;
440 object
->line
->y
[1] = final2
;
444 printf("fo %d %d %d %d\n", object
->line
->x
[0], object
->line
->y
[0],
445 object
->line
->x
[1], object
->line
->y
[1]);
448 /* Move any attributes from the deleted object*/
449 if (changed
&& del_object
->attribs
!= NULL
) {
451 /* Reassign the attached_to pointer on attributes from the del object */
452 a_iter
= del_object
->attribs
;
453 while (a_iter
!= NULL
) {
454 a_current
= a_iter
->data
;
455 a_current
->attached_to
= object
;
456 a_iter
= g_list_next (a_iter
);
459 object
->attribs
= g_list_concat (object
->attribs
, del_object
->attribs
);
461 /* Don't free del_object->attribs as it's relinked into object's list */
462 del_object
->attribs
= NULL
;
466 /*! \brief Check if there's a midpoint connection at (x,y)
467 * \par Function Description
468 * This function checks if the \a object is connected to another net
469 * between it's endpoints. Net segment's only can be merged if there
470 * is no midpoint connection.
472 * \param object a net OBJECT to check
473 * \param x x-coord of the connection location
474 * \param y y-coord of the connection location
475 * \return TRUE if there's no midpoint connection, else return FALSE
477 static int o_net_consolidate_nomidpoint (OBJECT
*object
, int x
, int y
)
482 c_current
= object
->conn_list
;
483 while(c_current
!= NULL
) {
484 conn
= (CONN
*) c_current
->data
;
485 if (conn
->other_object
) {
486 if (conn
->other_object
->sid
!= object
->sid
&&
487 conn
->x
== x
&& conn
->y
== y
&&
488 conn
->type
== CONN_MIDPOINT
) {
490 printf("Found one! %s\n", conn
->other_object
->name
);
496 c_current
= g_list_next(c_current
);
502 /*! \brief try to consolidate a net object
503 * \par Function Description
504 * This function tries to consolidate a net with any other object
505 * that is connected to the current \a object.
507 * \param toplevel The TOPLEVEL object
508 * \param object The object to consolidate
509 * \return 0 if no consolidation was possible, -1 otherwise
512 static int o_net_consolidate_segments (TOPLEVEL
*toplevel
, OBJECT
*object
)
518 OBJECT
*other_object
;
522 g_return_val_if_fail ((toplevel
!= NULL
), 0);
523 g_return_val_if_fail ((object
!= NULL
), 0);
524 g_return_val_if_fail ((object
->type
== OBJ_NET
), 0);
526 /* It's meaningless to do anything here if the object isn't in a page. */
527 page
= o_get_page (toplevel
, object
);
528 g_return_val_if_fail ((page
!= NULL
), 0);
530 object_orient
= o_net_orientation(object
);
532 c_current
= object
->conn_list
;
533 while(c_current
!= NULL
) {
534 conn
= (CONN
*) c_current
->data
;
535 other_object
= conn
->other_object
;
537 /* only look at end points which have a valid end on the other side */
538 if (other_object
!= NULL
&& conn
->type
== CONN_ENDPOINT
&&
539 conn
->other_whichone
!= -1 && conn
->whichone
!= -1 &&
540 o_net_consolidate_nomidpoint(object
, conn
->x
, conn
->y
) ) {
542 if (other_object
->type
== OBJ_NET
) {
543 other_orient
= o_net_orientation(other_object
);
545 /* - both objects have the same orientation (either vert or horiz) */
546 /* - it's not the same object */
547 if (object_orient
== other_orient
&&
548 object
->sid
!= other_object
->sid
&&
549 other_orient
!= NEITHER
) {
552 printf("consolidating %s to %s\n", object
->name
, other_object
->name
);
555 o_net_consolidate_lowlevel(object
, other_object
, other_orient
);
558 if (other_object
->selected
== TRUE
) {
559 o_selection_remove (toplevel
, page
->selection_list
, other_object
);
561 /* If we're consolidating with a selected object,
562 * ensure we select the resulting object.
564 if (object
->selected
== FALSE
) {
565 o_selection_add (toplevel
, page
->selection_list
, object
);
569 s_delete_object (toplevel
, other_object
);
570 o_net_recalc(toplevel
, object
);
571 s_tile_update_object(toplevel
, object
);
572 s_conn_update_object (toplevel
, object
);
579 c_current
= g_list_next (c_current
);
585 /*! \brief consolidate all net objects
586 * \par Function Description
587 * This function consolidates all net objects in a page until no more
588 * consolidations are possible.
590 * \param toplevel The TOPLEVEL object.
591 * \param page The PAGE to consolidate nets in.
593 void o_net_consolidate(TOPLEVEL
*toplevel
, PAGE
*page
)
599 g_return_if_fail (toplevel
!= NULL
);
600 g_return_if_fail (page
!= NULL
);
602 iter
= s_page_objects (page
);
604 while (iter
!= NULL
) {
605 o_current
= (OBJECT
*)iter
->data
;
607 if (o_current
->type
== OBJ_NET
) {
608 status
= o_net_consolidate_segments(toplevel
, o_current
);
612 iter
= s_page_objects (page
);
615 iter
= g_list_next (iter
);
620 /*! \brief modify one point of a net object
621 * \par Function Description
622 * This function modifies one point of a net \a object. The point
623 * is specified by the \a whichone variable and the new coordinate
626 * \param toplevel The TOPLEVEL object
627 * \param object The net OBJECT to modify
628 * \param x new x-coord of the net point
629 * \param y new y-coord of the net point
630 * \param whichone net point to modify
633 void o_net_modify(TOPLEVEL
*toplevel
, OBJECT
*object
,
634 int x
, int y
, int whichone
)
636 object
->line
->x
[whichone
] = x
;
637 object
->line
->y
[whichone
] = y
;
639 o_net_recalc (toplevel
, object
);
641 s_tile_update_object(toplevel
, object
);
644 /*! \brief Refresh & cache number of connected entities.
646 * \par Function Description
647 * Traverse all network segments reachable from this net and count
648 * total number of unique entities connected to all net segments.
650 * For the purpose of this function, an entity is:
653 * - attached netname attribute
655 * Computed number of entities is afterwards stored in all traversed
658 * The algorithm does not handle corner cases, ie two bus segments
659 * belonging to the same bus will be counted twice.
661 * \param [in] toplevel The TOPLEVEL object
662 * \param [in] o_current The NET OBJECT to check connectivity of
664 void o_net_refresh_conn_cache(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
673 g_return_if_fail (toplevel
);
674 g_return_if_fail (o_current
);
675 g_return_if_fail (o_current
->type
== OBJ_NET
);
677 /* Keep track of visited nets, pins and buses in the hash table.
678 * This way we short-circuit the search and avoid loops.
680 visited
= g_hash_table_new (NULL
, NULL
);
683 * Add the starting net to the hash so:
684 * 1. it is not traversed twice
685 * 2. it is updated at the end if no connections are found
687 g_hash_table_insert (visited
, o_current
, o_current
);
689 /* Check if a netname= is attached to the starting net segment */
690 if (NULL
!= o_attrib_search_object_attribs_by_name (o_current
,
696 /* Keep track of connections to search at each net segment in a stack.
697 * Each stack entry points to an entry in obj->conn_list.
698 * Pop the stack when we have no more connections to check.
699 * Push next entry on the stack if we encounter a net segment.
702 /* Initialise the stack for the starting net segment. */
703 stack
= g_list_prepend (stack
, o_current
->conn_list
);
705 while (stack
!= NULL
) {
706 /* At start of the loop, take a new connection from the stack. */
707 GList
*conn_list
= (GList
*) stack
->data
;
710 if (conn_list
== NULL
) {
711 /* No more connections to check at this level. Pop the stack. */
712 stack
= g_list_delete_link (stack
, stack
);
716 /* Extract the next connected object and advance the connection list. */
717 conn
= (CONN
*) conn_list
->data
;
718 stack
->data
= (gpointer
) g_list_next (conn_list
);
721 /* should not happen */
723 obj
= conn
->other_object
;
725 /* should not happen */
728 /* Act upon the object that is connected to the segment. */
732 if (NULL
== g_hash_table_lookup (visited
, obj
)) {
733 g_hash_table_insert (visited
, obj
, obj
);
738 if (NULL
== g_hash_table_lookup (visited
, obj
)) {
739 g_hash_table_insert (visited
, obj
, obj
);
740 /* Check if a netname= is attached to this net segment */
741 if (NULL
!= o_attrib_search_object_attribs_by_name (obj
,
746 /* Push new list of connections to check onto the stack */
747 stack
= g_list_prepend (stack
, obj
->conn_list
);
755 /* Cache value of num_conns in all visited objects */
756 g_hash_table_iter_init (&iter
, visited
);
758 while (g_hash_table_iter_next (&iter
, &key
, NULL
)) {
760 if (obj
->type
== OBJ_NET
) {
761 obj
->net_num_connected
= num_conns
;
762 obj
->valid_num_connected
= TRUE
;
766 g_hash_table_destroy (visited
);
770 /*! \brief Check if net is fully connected.
772 * \par Function Description
773 * Net is fully connected when it connects at least
774 * two separate entities.
776 * \sa o_net_refresh_conn_cache
778 * \param [in] toplevel The TOPLEVEL object
779 * \param [in] o_current The OBJECT to check connectivity of
780 * \return TRUE if net is fully connected, FALSE otherwise
782 gboolean
o_net_is_fully_connected (TOPLEVEL
*toplevel
,
785 g_return_val_if_fail (toplevel
, FALSE
);
786 g_return_val_if_fail (o_current
, FALSE
);
787 g_return_val_if_fail (o_current
->type
== OBJ_NET
, FALSE
);
789 if (!o_current
->valid_num_connected
)
790 o_net_refresh_conn_cache (toplevel
, o_current
);
792 g_return_val_if_fail (o_current
->valid_num_connected
, FALSE
);
794 return o_current
->net_num_connected
> 1 ? TRUE
: FALSE
;