refdes_renum: warn of possible number clash with non-conforming values
[geda-gaf/whiteaudio.git] / libgeda / src / o_net_basic.c
blob96f063880fe9c60bbe88a2691bc5433b283a0699
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
20 #include <config.h>
22 #include <stdio.h>
23 #include <math.h>
25 #include "libgeda_priv.h"
27 #ifdef HAVE_LIBDMALLOC
28 #include <dmalloc.h>
29 #endif
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,
46 OBJECT *object)
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)
84 OBJECT *new_node;
86 new_node = s_basic_new_object(type, "net");
87 new_node->color = color;
89 new_node->line = (LINE *) g_malloc(sizeof(LINE));
90 /* check for null */
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);
100 return 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) {
116 return;
119 if (o_current->line == NULL) {
120 return;
123 world_get_net_bounds(toplevel, o_current, &left, &top, &right,
124 &bottom);
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)
149 OBJECT *new_obj;
150 char type;
151 int x1, y1;
152 int x2, y2;
153 int color;
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"));
157 return NULL;
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);
178 return new_obj;
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)
192 int x1, x2, y1, y2;
193 char *buf;
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);
201 return (buf);
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,
215 OBJECT *object)
217 if (object == NULL)
218 printf("ntw NO!\n");
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)
242 OBJECT *new_obj;
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]);
252 return new_obj;
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)
269 int net_width;
270 int x1, y1;
271 int x2, y2;
273 if (o_current == NULL) {
274 printf("got null in o_net_print\n");
275 return;
278 f_print_set_color(toplevel, fp, o_current->color);
280 net_width = 2;
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,
308 OBJECT *object)
310 int newx, newy;
312 if (angle == 0)
313 return;
315 /* translate object to origin */
316 o_net_translate_world(toplevel, -world_centerx, -world_centery,
317 object);
319 rotate_point_90(object->line->x[0], object->line->y[0], angle,
320 &newx, &newy);
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,
326 &newx, &newy);
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,
349 object);
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]) {
368 return (HORIZONTAL);
371 if (object->line->x[0] == object->line->x[1]) {
372 return (VERTICAL);
375 return (NEITHER);
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
383 * both objects.
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)
396 int temp1, temp2;
397 int final1, final2;
398 int changed = 0;
399 GList *a_iter;
400 OBJECT *a_current;
402 #if DEBUG
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]);
408 #endif
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;
425 changed = 1;
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;
441 changed = 1;
443 #if DEBUG
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]);
446 #endif
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)
479 GList *c_current;
480 CONN *conn;
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) {
489 #if DEBUG
490 printf("Found one! %s\n", conn->other_object->name);
491 #endif
492 return(FALSE);
496 c_current = g_list_next(c_current);
499 return(TRUE);
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)
514 int object_orient;
515 int other_orient;
516 GList *c_current;
517 CONN *conn;
518 OBJECT *other_object;
519 PAGE *page;
520 int changed = 0;
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) {
551 #if DEBUG
552 printf("consolidating %s to %s\n", object->name, other_object->name);
553 #endif
555 o_net_consolidate_lowlevel(object, other_object, other_orient);
557 changed++;
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);
573 return(-1);
579 c_current = g_list_next (c_current);
582 return(0);
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)
595 OBJECT *o_current;
596 const GList *iter;
597 int status = 0;
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);
611 if (status == -1) {
612 iter = s_page_objects (page);
613 status = 0;
614 } else {
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
624 * is (\a x, \a y).
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:
651 * - pin
652 * - bus
653 * - attached netname attribute
655 * Computed number of entities is afterwards stored in all traversed
656 * net segments.
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)
666 gint num_conns = 0;
667 GHashTable *visited;
668 GHashTableIter iter;
669 GList *stack = NULL;
670 OBJECT *obj;
671 gpointer key;
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,
691 "netname",
692 0)) {
693 num_conns += 1;
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;
708 CONN *conn;
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);
713 continue;
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);
720 if (conn == NULL)
721 /* should not happen */
722 continue;
723 obj = conn->other_object;
724 if (obj == NULL)
725 /* should not happen */
726 continue;
728 /* Act upon the object that is connected to the segment. */
729 switch (obj->type) {
730 case OBJ_PIN:
731 case OBJ_BUS:
732 if (NULL == g_hash_table_lookup (visited, obj)) {
733 g_hash_table_insert (visited, obj, obj);
734 num_conns += 1;
736 break;
737 case OBJ_NET:
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,
742 "netname",
743 0)) {
744 num_conns += 1;
746 /* Push new list of connections to check onto the stack */
747 stack = g_list_prepend (stack, obj->conn_list);
749 break;
750 default:
751 break;
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)) {
759 obj = (OBJECT*) key;
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);
767 g_list_free (stack);
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,
783 OBJECT *o_current)
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;