libgeda
|
00001 /* gEDA - GPL Electronic Design Automation 00002 * libgeda - gEDA's library 00003 * Copyright (C) 1998-2010 Ales Hvezda 00004 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details) 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 #include <config.h> 00021 00022 #include <stdio.h> 00023 #include <math.h> 00024 00025 #include "libgeda_priv.h" 00026 00027 #ifdef HAVE_LIBDMALLOC 00028 #include <dmalloc.h> 00029 #endif 00030 00045 gboolean o_net_get_position (TOPLEVEL *toplevel, gint *x, gint *y, 00046 OBJECT *object) 00047 { 00048 return o_line_get_position(toplevel, x, y, object); 00049 } 00050 00062 void world_get_net_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, 00063 int *top, int *right, int *bottom) 00064 { 00065 world_get_line_bounds( toplevel, object, left, top, right, bottom ); 00066 } 00067 00081 OBJECT *o_net_new(TOPLEVEL *toplevel, char type, 00082 int color, int x1, int y1, int x2, int y2) 00083 { 00084 OBJECT *new_node; 00085 00086 new_node = s_basic_new_object(type, "net"); 00087 new_node->color = color; 00088 00089 new_node->line = (LINE *) g_malloc(sizeof(LINE)); 00090 /* check for null */ 00091 00092 new_node->line->x[0] = x1; 00093 new_node->line->y[0] = y1; 00094 new_node->line->x[1] = x2; 00095 new_node->line->y[1] = y2; 00096 new_node->line_width = NET_WIDTH; 00097 00098 o_net_recalc (toplevel, new_node); 00099 00100 return new_node; 00101 } 00102 00111 void o_net_recalc(TOPLEVEL *toplevel, OBJECT *o_current) 00112 { 00113 int left, right, top, bottom; 00114 00115 if (o_current == NULL) { 00116 return; 00117 } 00118 00119 if (o_current->line == NULL) { 00120 return; 00121 } 00122 00123 world_get_net_bounds(toplevel, o_current, &left, &top, &right, 00124 &bottom); 00125 00126 o_current->w_left = left; 00127 o_current->w_top = top; 00128 o_current->w_right = right; 00129 o_current->w_bottom = bottom; 00130 o_current->w_bounds_valid = TRUE; 00131 } 00132 00146 OBJECT *o_net_read (TOPLEVEL *toplevel, const char buf[], 00147 unsigned int release_ver, unsigned int fileformat_ver, GError **err) 00148 { 00149 OBJECT *new_obj; 00150 char type; 00151 int x1, y1; 00152 int x2, y2; 00153 int color; 00154 00155 if (sscanf (buf, "%c %d %d %d %d %d\n", &type, &x1, &y1, &x2, &y2, &color) != 6) { 00156 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse net object")); 00157 return NULL; 00158 } 00159 00160 if (x1 == x2 && y1 == y2) { 00161 s_log_message (_("Found a zero length net [ %c %d %d %d %d %d ]\n"), 00162 type, x1, y1, x2, y2, color); 00163 } 00164 00165 00166 if (toplevel->override_net_color != -1) { 00167 color = toplevel->override_net_color; 00168 } 00169 00170 if (color < 0 || color > MAX_COLORS) { 00171 s_log_message (_("Found an invalid color [ %s ]\n"), buf); 00172 s_log_message (_("Setting color to default color\n")); 00173 color = DEFAULT_COLOR; 00174 } 00175 00176 new_obj = o_net_new (toplevel, type, color, x1, y1, x2, y2); 00177 00178 return new_obj; 00179 } 00180 00190 char *o_net_save(TOPLEVEL *toplevel, OBJECT *object) 00191 { 00192 int x1, x2, y1, y2; 00193 char *buf; 00194 00195 x1 = object->line->x[0]; 00196 y1 = object->line->y[0]; 00197 x2 = object->line->x[1]; 00198 y2 = object->line->y[1]; 00199 00200 buf = g_strdup_printf("%c %d %d %d %d %d", object->type, x1, y1, x2, y2, object->color); 00201 return (buf); 00202 } 00203 00214 void o_net_translate_world(TOPLEVEL *toplevel, int dx, int dy, 00215 OBJECT *object) 00216 { 00217 /* Update world coords */ 00218 object->line->x[0] = object->line->x[0] + dx; 00219 object->line->y[0] = object->line->y[0] + dy; 00220 object->line->x[1] = object->line->x[1] + dx; 00221 object->line->y[1] = object->line->y[1] + dy; 00222 00223 /* Update bounding box */ 00224 o_net_recalc (toplevel, object); 00225 00226 s_tile_update_object(toplevel, object); 00227 } 00228 00237 OBJECT *o_net_copy(TOPLEVEL *toplevel, OBJECT *o_current) 00238 { 00239 OBJECT *new_obj; 00240 00241 /* make sure you fix this in pin and bus as well */ 00242 /* still doesn't work... you need to pass in the new values */ 00243 /* or don't update and update later */ 00244 /* I think for now I'll disable the update and manually update */ 00245 new_obj = o_net_new (toplevel, OBJ_NET, o_current->color, 00246 o_current->line->x[0], o_current->line->y[0], 00247 o_current->line->x[1], o_current->line->y[1]); 00248 00249 return new_obj; 00250 } 00251 00263 void o_net_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current, 00264 int origin_x, int origin_y) 00265 { 00266 int net_width; 00267 int x1, y1; 00268 int x2, y2; 00269 00270 if (o_current == NULL) { 00271 printf("got null in o_net_print\n"); 00272 return; 00273 } 00274 00275 f_print_set_color(toplevel, fp, o_current->color); 00276 00277 net_width = 2; 00278 if (toplevel->net_style == THICK) { 00279 net_width = NET_WIDTH; 00280 } 00281 00282 x1 = o_current->line->x[0] - origin_x, 00283 y1 = o_current->line->y[0] - origin_y; 00284 x2 = o_current->line->x[1] - origin_x, 00285 y2 = o_current->line->y[1] - origin_y; 00286 00287 fprintf(fp, "%d %d %d %d %d line\n", x1,y1,x2,y2,net_width); 00288 } 00289 00290 00303 void o_net_rotate_world(TOPLEVEL *toplevel, 00304 int world_centerx, int world_centery, int angle, 00305 OBJECT *object) 00306 { 00307 int newx, newy; 00308 00309 if (angle == 0) 00310 return; 00311 00312 /* translate object to origin */ 00313 o_net_translate_world(toplevel, -world_centerx, -world_centery, 00314 object); 00315 00316 rotate_point_90(object->line->x[0], object->line->y[0], angle, 00317 &newx, &newy); 00318 00319 object->line->x[0] = newx; 00320 object->line->y[0] = newy; 00321 00322 rotate_point_90(object->line->x[1], object->line->y[1], angle, 00323 &newx, &newy); 00324 00325 object->line->x[1] = newx; 00326 object->line->y[1] = newy; 00327 00328 o_net_translate_world(toplevel, world_centerx, world_centery, object); 00329 } 00330 00341 void o_net_mirror_world(TOPLEVEL *toplevel, int world_centerx, 00342 int world_centery, OBJECT *object) 00343 { 00344 /* translate object to origin */ 00345 o_net_translate_world(toplevel, -world_centerx, -world_centery, 00346 object); 00347 00348 object->line->x[0] = -object->line->x[0]; 00349 00350 object->line->x[1] = -object->line->x[1]; 00351 00352 o_net_translate_world(toplevel, world_centerx, world_centery, object); 00353 } 00354 00362 int o_net_orientation(OBJECT *object) 00363 { 00364 if (object->line->y[0] == object->line->y[1]) { 00365 return (HORIZONTAL); 00366 } 00367 00368 if (object->line->x[0] == object->line->x[1]) { 00369 return (VERTICAL); 00370 } 00371 00372 return (NEITHER); 00373 } 00374 00375 00390 static void o_net_consolidate_lowlevel (OBJECT *object, 00391 OBJECT *del_object, int orient) 00392 { 00393 int temp1, temp2; 00394 int final1, final2; 00395 int changed = 0; 00396 GList *a_iter; 00397 OBJECT *a_current; 00398 00399 #if DEBUG 00400 printf("o %d %d %d %d\n", object->line->x[0], object->line->y[0], 00401 object->line->x[1], object->line->y[1]); 00402 printf("d %d %d %d %d\n", del_object->line->x[0], 00403 del_object->line->y[0], del_object->line->x[1], 00404 del_object->line->y[1]); 00405 #endif 00406 00407 00408 if (orient == HORIZONTAL) { 00409 00410 temp1 = min(object->line->x[0], del_object->line->x[0]); 00411 temp2 = min(object->line->x[1], del_object->line->x[1]); 00412 00413 final1 = min(temp1, temp2); 00414 00415 temp1 = max(object->line->x[0], del_object->line->x[0]); 00416 temp2 = max(object->line->x[1], del_object->line->x[1]); 00417 00418 final2 = max(temp1, temp2); 00419 00420 object->line->x[0] = final1; 00421 object->line->x[1] = final2; 00422 changed = 1; 00423 } 00424 00425 if (orient == VERTICAL) { 00426 temp1 = min(object->line->y[0], del_object->line->y[0]); 00427 temp2 = min(object->line->y[1], del_object->line->y[1]); 00428 00429 final1 = min(temp1, temp2); 00430 00431 temp1 = max(object->line->y[0], del_object->line->y[0]); 00432 temp2 = max(object->line->y[1], del_object->line->y[1]); 00433 00434 final2 = max(temp1, temp2); 00435 00436 object->line->y[0] = final1; 00437 object->line->y[1] = final2; 00438 changed = 1; 00439 } 00440 #if DEBUG 00441 printf("fo %d %d %d %d\n", object->line->x[0], object->line->y[0], 00442 object->line->x[1], object->line->y[1]); 00443 #endif 00444 00445 /* Move any attributes from the deleted object*/ 00446 if (changed && del_object->attribs != NULL) { 00447 00448 /* Reassign the attached_to pointer on attributes from the del object */ 00449 a_iter = del_object->attribs; 00450 while (a_iter != NULL) { 00451 a_current = a_iter->data; 00452 a_current->attached_to = object; 00453 a_iter = g_list_next (a_iter); 00454 } 00455 00456 object->attribs = g_list_concat (object->attribs, del_object->attribs); 00457 00458 /* Don't free del_object->attribs as it's relinked into object's list */ 00459 del_object->attribs = NULL; 00460 } 00461 } 00462 00474 static int o_net_consolidate_nomidpoint (OBJECT *object, int x, int y) 00475 { 00476 GList *c_current; 00477 CONN *conn; 00478 00479 c_current = object->conn_list; 00480 while(c_current != NULL) { 00481 conn = (CONN *) c_current->data; 00482 if (conn->other_object) { 00483 if (conn->other_object->sid != object->sid && 00484 conn->x == x && conn->y == y && 00485 conn->type == CONN_MIDPOINT) { 00486 #if DEBUG 00487 printf("Found one! %s\n", conn->other_object->name); 00488 #endif 00489 return(FALSE); 00490 } 00491 } 00492 00493 c_current = g_list_next(c_current); 00494 } 00495 00496 return(TRUE); 00497 } 00498 00509 static int o_net_consolidate_segments (TOPLEVEL *toplevel, OBJECT *object) 00510 { 00511 int object_orient; 00512 int other_orient; 00513 GList *c_current; 00514 CONN *conn; 00515 OBJECT *other_object; 00516 PAGE *page; 00517 int changed = 0; 00518 00519 g_return_val_if_fail ((toplevel != NULL), 0); 00520 g_return_val_if_fail ((object != NULL), 0); 00521 g_return_val_if_fail ((object->type == OBJ_NET), 0); 00522 00523 /* It's meaningless to do anything here if the object isn't in a page. */ 00524 page = o_get_page (toplevel, object); 00525 g_return_val_if_fail ((page != NULL), 0); 00526 00527 object_orient = o_net_orientation(object); 00528 00529 c_current = object->conn_list; 00530 while(c_current != NULL) { 00531 conn = (CONN *) c_current->data; 00532 other_object = conn->other_object; 00533 00534 /* only look at end points which have a valid end on the other side */ 00535 if (other_object != NULL && conn->type == CONN_ENDPOINT && 00536 conn->other_whichone != -1 && conn->whichone != -1 && 00537 o_net_consolidate_nomidpoint(object, conn->x, conn->y) ) { 00538 00539 if (other_object->type == OBJ_NET) { 00540 other_orient = o_net_orientation(other_object); 00541 00542 /* - both objects have the same orientation (either vert or horiz) */ 00543 /* - it's not the same object */ 00544 if (object_orient == other_orient && 00545 object->sid != other_object->sid && 00546 other_orient != NEITHER) { 00547 00548 #if DEBUG 00549 printf("consolidating %s to %s\n", object->name, other_object->name); 00550 #endif 00551 00552 o_net_consolidate_lowlevel(object, other_object, other_orient); 00553 00554 changed++; 00555 if (other_object->selected == TRUE ) { 00556 o_selection_remove (toplevel, page->selection_list, other_object); 00557 00558 /* If we're consolidating with a selected object, 00559 * ensure we select the resulting object. 00560 */ 00561 if (object->selected == FALSE) { 00562 o_selection_add (toplevel, page->selection_list, object); 00563 } 00564 } 00565 00566 s_delete_object (toplevel, other_object); 00567 o_net_recalc(toplevel, object); 00568 s_tile_update_object(toplevel, object); 00569 s_conn_update_object (toplevel, object); 00570 return(-1); 00571 } 00572 } 00573 00574 } 00575 00576 c_current = g_list_next (c_current); 00577 } 00578 00579 return(0); 00580 } 00581 00590 void o_net_consolidate(TOPLEVEL *toplevel, PAGE *page) 00591 { 00592 OBJECT *o_current; 00593 const GList *iter; 00594 int status = 0; 00595 00596 g_return_if_fail (toplevel != NULL); 00597 g_return_if_fail (page != NULL); 00598 00599 iter = s_page_objects (page); 00600 00601 while (iter != NULL) { 00602 o_current = (OBJECT *)iter->data; 00603 00604 if (o_current->type == OBJ_NET) { 00605 status = o_net_consolidate_segments(toplevel, o_current); 00606 } 00607 00608 if (status == -1) { 00609 iter = s_page_objects (page); 00610 status = 0; 00611 } else { 00612 iter = g_list_next (iter); 00613 } 00614 } 00615 } 00616 00630 void o_net_modify(TOPLEVEL *toplevel, OBJECT *object, 00631 int x, int y, int whichone) 00632 { 00633 object->line->x[whichone] = x; 00634 object->line->y[whichone] = y; 00635 00636 o_net_recalc (toplevel, object); 00637 00638 s_tile_update_object(toplevel, object); 00639 } 00640 00661 void o_net_refresh_conn_cache(TOPLEVEL *toplevel, OBJECT *o_current) 00662 { 00663 gint num_conns = 0; 00664 GHashTable *visited; 00665 GHashTableIter iter; 00666 GList *stack = NULL; 00667 OBJECT *obj; 00668 gpointer key; 00669 00670 g_return_if_fail (toplevel); 00671 g_return_if_fail (o_current); 00672 g_return_if_fail (o_current->type == OBJ_NET); 00673 00674 /* Keep track of visited nets, pins and buses in the hash table. 00675 * This way we short-circuit the search and avoid loops. 00676 */ 00677 visited = g_hash_table_new (NULL, NULL); 00678 00679 /* 00680 * Add the starting net to the hash so: 00681 * 1. it is not traversed twice 00682 * 2. it is updated at the end if no connections are found 00683 */ 00684 g_hash_table_insert (visited, o_current, o_current); 00685 00686 /* Check if a netname= is attached to the starting net segment */ 00687 if (NULL != o_attrib_search_object_attribs_by_name (o_current, 00688 "netname", 00689 0)) { 00690 num_conns += 1; 00691 } 00692 00693 /* Keep track of connections to search at each net segment in a stack. 00694 * Each stack entry points to an entry in obj->conn_list. 00695 * Pop the stack when we have no more connections to check. 00696 * Push next entry on the stack if we encounter a net segment. 00697 */ 00698 00699 /* Initialise the stack for the starting net segment. */ 00700 stack = g_list_prepend (stack, o_current->conn_list); 00701 00702 while (stack != NULL) { 00703 /* At start of the loop, take a new connection from the stack. */ 00704 GList *conn_list = (GList*) stack->data; 00705 CONN *conn; 00706 00707 if (conn_list == NULL) { 00708 /* No more connections to check at this level. Pop the stack. */ 00709 stack = g_list_delete_link (stack, stack); 00710 continue; 00711 } 00712 00713 /* Extract the next connected object and advance the connection list. */ 00714 conn = (CONN*) conn_list->data; 00715 stack->data = (gpointer) g_list_next (conn_list); 00716 00717 if (conn == NULL) 00718 /* should not happen */ 00719 continue; 00720 obj = conn->other_object; 00721 if (obj == NULL) 00722 /* should not happen */ 00723 continue; 00724 00725 /* Act upon the object that is connected to the segment. */ 00726 switch (obj->type) { 00727 case OBJ_PIN: 00728 case OBJ_BUS: 00729 if (NULL == g_hash_table_lookup (visited, obj)) { 00730 g_hash_table_insert (visited, obj, obj); 00731 num_conns += 1; 00732 } 00733 break; 00734 case OBJ_NET: 00735 if (NULL == g_hash_table_lookup (visited, obj)) { 00736 g_hash_table_insert (visited, obj, obj); 00737 /* Check if a netname= is attached to this net segment */ 00738 if (NULL != o_attrib_search_object_attribs_by_name (obj, 00739 "netname", 00740 0)) { 00741 num_conns += 1; 00742 } 00743 /* Push new list of connections to check onto the stack */ 00744 stack = g_list_prepend (stack, obj->conn_list); 00745 } 00746 break; 00747 default: 00748 break; 00749 } 00750 } 00751 00752 /* Cache value of num_conns in all visited objects */ 00753 g_hash_table_iter_init (&iter, visited); 00754 00755 while (g_hash_table_iter_next (&iter, &key, NULL)) { 00756 obj = (OBJECT*) key; 00757 if (obj->type == OBJ_NET) { 00758 obj->net_num_connected = num_conns; 00759 obj->valid_num_connected = TRUE; 00760 } 00761 } 00762 00763 g_hash_table_destroy (visited); 00764 g_list_free (stack); 00765 } 00766 00779 gboolean o_net_is_fully_connected (TOPLEVEL *toplevel, 00780 OBJECT *o_current) 00781 { 00782 g_return_val_if_fail (toplevel, FALSE); 00783 g_return_val_if_fail (o_current, FALSE); 00784 g_return_val_if_fail (o_current->type == OBJ_NET, FALSE); 00785 00786 if (!o_current->valid_num_connected) 00787 o_net_refresh_conn_cache (toplevel, o_current); 00788 00789 g_return_val_if_fail (o_current->valid_num_connected, FALSE); 00790 00791 return o_current->net_num_connected > 1 ? TRUE : FALSE; 00792 }