(no commit message)
[geda-pcb/pcjc2.git] / src / rats.c
blob5c48055613de92df4a2bb55f486c404f1dfb2ad1
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
6 *
7 * This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
8 * this module is also subject to the GNU GPL as described below
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 /* Change History:
27 * Started 6/10/97
28 * Added support for minimum length rat lines 6/13/97
29 * rat lines to nearest line/via 8/29/98
30 * support for netlist window 10/24/98
33 /* rats nest routines
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
39 #include <math.h>
40 #include <stdio.h>
42 #include "global.h"
44 #include "create.h"
45 #include "data.h"
46 #include "draw.h"
47 #include "error.h"
48 #include "file.h"
49 #include "find.h"
50 #include "misc.h"
51 #include "mymem.h"
52 #include "polygon.h"
53 #include "rats.h"
54 #include "search.h"
55 #include "set.h"
56 #include "undo.h"
58 #ifdef HAVE_LIBDMALLOC
59 #include <dmalloc.h>
60 #endif
62 #define TRIEDFIRST 0x1
63 #define BESTFOUND 0x2
65 /* ---------------------------------------------------------------------------
66 * some forward declarations
68 static bool FindPad (char *, char *, ConnectionType *, bool);
69 static bool ParseConnection (char *, char *, char *);
70 static bool DrawShortestRats (NetListType *, void (*)(register ConnectionType *, register ConnectionType *, register RouteStyleType *));
71 static bool GatherSubnets (NetListType *, bool, bool);
72 static bool CheckShorts (LibraryMenuType *);
73 static void TransferNet (NetListType *, NetType *, NetType *);
75 /* ---------------------------------------------------------------------------
76 * some local identifiers
78 static bool badnet = false;
79 static Cardinal top_group, bottom_group; /* layer group holding top/bottom side */
81 /* ---------------------------------------------------------------------------
82 * parse a connection description from a string
83 * puts the element name in the string and the pin number in
84 * the number. If a valid connection is found, it returns the
85 * number of characters processed from the string, otherwise
86 * it returns 0
88 static bool
89 ParseConnection (char *InString, char *ElementName, char *PinNum)
91 int i, j;
93 /* copy element name portion */
94 for (j = 0; InString[j] != '\0' && InString[j] != '-'; j++)
95 ElementName[j] = InString[j];
96 if (InString[j] == '-')
98 for (i = j; i > 0 && ElementName[i - 1] >= 'a'; i--);
99 ElementName[i] = '\0';
100 for (i = 0, j++; InString[j] != '\0'; i++, j++)
101 PinNum[i] = InString[j];
102 PinNum[i] = '\0';
103 return (false);
105 else
107 ElementName[j] = '\0';
108 Message (_("Bad net-list format encountered near: \"%s\"\n"),
109 ElementName);
110 return (true);
114 /* ---------------------------------------------------------------------------
115 * Find a particular pad from an element name and pin number
117 static bool
118 FindPad (char *ElementName, char *PinNum, ConnectionType * conn, bool Same)
120 ElementType *element;
121 GList *i;
123 if ((element = SearchElementByName (PCB->Data, ElementName)) == NULL)
124 return false;
126 for (i = element->Pad; i != NULL; i = g_list_next (i))
128 PadType *pad = i->data;
130 if (NSTRCMP (PinNum, pad->Number) == 0 &&
131 (!Same || !TEST_FLAG (DRCFLAG, pad)))
133 conn->type = PAD_TYPE;
134 conn->ptr1 = element;
135 conn->ptr2 = pad;
136 conn->group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
138 if (TEST_FLAG (EDGE2FLAG, pad))
140 conn->X = pad->Point2.X;
141 conn->Y = pad->Point2.Y;
143 else
145 conn->X = pad->Point1.X;
146 conn->Y = pad->Point1.Y;
148 return true;
152 for (i = element->Pin; i != NULL; i = g_list_next (i))
154 PinType *pin = i->data;
156 if (!TEST_FLAG (HOLEFLAG, pin) &&
157 pin->Number && NSTRCMP (PinNum, pin->Number) == 0 &&
158 (!Same || !TEST_FLAG (DRCFLAG, pin)))
160 conn->type = PIN_TYPE;
161 conn->ptr1 = element;
162 conn->ptr2 = pin;
163 conn->group = bottom_group; /* any layer will do */
164 conn->X = pin->X;
165 conn->Y = pin->Y;
166 return true;
170 return false;
173 /*--------------------------------------------------------------------------
174 * parse a netlist menu entry and locate the corresponding pad
175 * returns true if found, and fills in Connection information
177 bool
178 SeekPad (LibraryEntryType * entry, ConnectionType * conn, bool Same)
180 int j;
181 char ElementName[256];
182 char PinNum[256];
184 if (ParseConnection (entry->ListEntry, ElementName, PinNum))
185 return (false);
186 for (j = 0; PinNum[j] != '\0'; j++);
187 if (j == 0)
189 Message (_("Error! Netlist file is missing pin!\n"
190 "white space after \"%s-\"\n"), ElementName);
191 badnet = true;
193 else
195 if (FindPad (ElementName, PinNum, conn, Same))
196 return (true);
197 if (Same)
198 return (false);
199 if (PinNum[j - 1] < '0' || PinNum[j - 1] > '9')
201 Message ("WARNING! Pin number ending with '%c'"
202 " encountered in netlist file\n"
203 "Probably a bad netlist file format\n", PinNum[j - 1]);
206 Message (_("Can't find %s pin %s called for in netlist.\n"),
207 ElementName, PinNum);
208 return (false);
211 /* ---------------------------------------------------------------------------
212 * Read the library-netlist build a true Netlist structure
215 NetListType *
216 ProcNetlist (LibraryType *net_menu)
218 ConnectionType *connection;
219 ConnectionType LastPoint;
220 NetType *net;
221 static NetListType *Wantlist = NULL;
223 if (!net_menu->MenuN)
224 return (NULL);
225 FreeNetListMemory (Wantlist);
226 free (Wantlist);
227 badnet = false;
229 /* find layer groups of the component side and solder side */
230 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
231 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
233 Wantlist = (NetListType *)calloc (1, sizeof (NetListType));
234 if (Wantlist)
236 ALLPIN_LOOP (PCB->Data);
238 pin->Spare = NULL;
239 CLEAR_FLAG (DRCFLAG, pin);
241 ENDALL_LOOP;
242 ALLPAD_LOOP (PCB->Data);
244 pad->Spare = NULL;
245 CLEAR_FLAG (DRCFLAG, pad);
247 ENDALL_LOOP;
248 MENU_LOOP (net_menu);
250 if (menu->Name[0] == '*' || menu->flag == 0)
252 badnet = true;
253 continue;
255 net = GetNetMemory (Wantlist);
256 if (menu->Style)
258 STYLE_LOOP (PCB);
260 if (style->Name && !NSTRCMP (style->Name, menu->Style))
262 net->Style = style;
263 break;
266 END_LOOP;
268 else /* default to NULL if none found */
269 net->Style = NULL;
270 ENTRY_LOOP (menu);
272 if (SeekPad (entry, &LastPoint, false))
274 if (TEST_FLAG (DRCFLAG, (PinType *) LastPoint.ptr2))
275 Message (_
276 ("Error! Element %s pin %s appears multiple times in the netlist file.\n"),
277 NAMEONPCB_NAME ((ElementType *) LastPoint.ptr1),
278 (LastPoint.type ==
279 PIN_TYPE) ? ((PinType *) LastPoint.ptr2)->
280 Number : ((PadType *) LastPoint.ptr2)->Number);
281 else
283 connection = GetConnectionMemory (net);
284 *connection = LastPoint;
285 /* indicate expect net */
286 connection->menu = menu;
287 /* mark as visited */
288 SET_FLAG (DRCFLAG, (PinType *) LastPoint.ptr2);
289 if (LastPoint.type == PIN_TYPE)
290 ((PinType *) LastPoint.ptr2)->Spare = (void *) menu;
291 else
292 ((PadType *) LastPoint.ptr2)->Spare = (void *) menu;
295 else
296 badnet = true;
297 /* check for more pins with the same number */
298 for (; SeekPad (entry, &LastPoint, true);)
300 connection = GetConnectionMemory (net);
301 *connection = LastPoint;
302 /* indicate expect net */
303 connection->menu = menu;
304 /* mark as visited */
305 SET_FLAG (DRCFLAG, (PinType *) LastPoint.ptr2);
306 if (LastPoint.type == PIN_TYPE)
307 ((PinType *) LastPoint.ptr2)->Spare = (void *) menu;
308 else
309 ((PadType *) LastPoint.ptr2)->Spare = (void *) menu;
312 END_LOOP;
314 END_LOOP;
316 /* clear all visit marks */
317 ALLPIN_LOOP (PCB->Data);
319 CLEAR_FLAG (DRCFLAG, pin);
321 ENDALL_LOOP;
322 ALLPAD_LOOP (PCB->Data);
324 CLEAR_FLAG (DRCFLAG, pad);
326 ENDALL_LOOP;
327 return (Wantlist);
331 * copy all connections from one net into another
332 * and then remove the first net from its netlist
334 static void
335 TransferNet (NetListType *Netl, NetType *SourceNet, NetType *DestNet)
337 ConnectionType *conn;
339 /* It would be worth checking if SourceNet is NULL here to avoid a segfault. Seb James. */
340 CONNECTION_LOOP (SourceNet);
342 conn = GetConnectionMemory (DestNet);
343 *conn = *connection;
345 END_LOOP;
346 DestNet->Style = SourceNet->Style;
347 /* free the connection memory */
348 FreeNetMemory (SourceNet);
349 /* remove SourceNet from its netlist */
350 *SourceNet = Netl->Net[--(Netl->NetN)];
351 /* zero out old garbage */
352 memset (&Netl->Net[Netl->NetN], 0, sizeof (NetType));
355 static bool
356 CheckShorts (LibraryMenuType *theNet)
358 bool newone, warn = false;
359 PointerListType *generic = (PointerListType *)calloc (1, sizeof (PointerListType));
360 /* the first connection was starting point so
361 * the menu is always non-null
363 void **menu = GetPointerMemory (generic);
365 *menu = theNet;
366 ALLPIN_LOOP (PCB->Data);
368 if (TEST_FLAG (DRCFLAG, pin))
370 warn = true;
371 if (!pin->Spare)
373 Message (_("Warning! Net \"%s\" is shorted to %s pin %s\n"),
374 &theNet->Name[2],
375 UNKNOWN (NAMEONPCB_NAME (element)),
376 UNKNOWN (pin->Number));
377 SET_FLAG (WARNFLAG, pin);
378 continue;
380 newone = true;
381 POINTER_LOOP (generic);
383 if (*ptr == pin->Spare)
385 newone = false;
386 break;
389 END_LOOP;
390 if (newone)
392 menu = GetPointerMemory (generic);
393 *menu = pin->Spare;
394 Message (_("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
395 &theNet->Name[2],
396 &((LibraryMenuType *) (pin->Spare))->Name[2]);
397 SET_FLAG (WARNFLAG, pin);
401 ENDALL_LOOP;
402 ALLPAD_LOOP (PCB->Data);
404 if (TEST_FLAG (DRCFLAG, pad))
406 warn = true;
407 if (!pad->Spare)
409 Message (_("Warning! Net \"%s\" is shorted to %s pad %s\n"),
410 &theNet->Name[2],
411 UNKNOWN (NAMEONPCB_NAME (element)),
412 UNKNOWN (pad->Number));
413 SET_FLAG (WARNFLAG, pad);
414 continue;
416 newone = true;
417 POINTER_LOOP (generic);
419 if (*ptr == pad->Spare)
421 newone = false;
422 break;
425 END_LOOP;
426 if (newone)
428 menu = GetPointerMemory (generic);
429 *menu = pad->Spare;
430 Message (_("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
431 &theNet->Name[2],
432 &((LibraryMenuType *) (pad->Spare))->Name[2]);
433 SET_FLAG (WARNFLAG, pad);
437 ENDALL_LOOP;
438 FreePointerListMemory (generic);
439 free (generic);
440 return (warn);
444 /* ---------------------------------------------------------------------------
445 * Determine existing interconnections of the net and gather into sub-nets
447 * initially the netlist has each connection in its own individual net
448 * afterwards there can be many fewer nets with multiple connections each
450 static bool
451 GatherSubnets (NetListType *Netl, bool NoWarn, bool AndRats)
453 NetType *a, *b;
454 ConnectionType *conn;
455 Cardinal m, n;
456 bool Warned = false;
458 for (m = 0; Netl->NetN > 0 && m < Netl->NetN; m++)
460 a = &Netl->Net[m];
461 ClearFlagOnAllObjects (false, DRCFLAG);
462 RatFindHook (a->Connection[0].type, a->Connection[0].ptr1,
463 a->Connection[0].ptr2, a->Connection[0].ptr2,
464 false, DRCFLAG, AndRats);
465 /* now anybody connected to the first point has DRCFLAG set */
466 /* so move those to this subnet */
467 CLEAR_FLAG (DRCFLAG, (PinType *) a->Connection[0].ptr2);
468 for (n = m + 1; n < Netl->NetN; n++)
470 b = &Netl->Net[n];
471 /* There can be only one connection in net b */
472 if (TEST_FLAG (DRCFLAG, (PinType *) b->Connection[0].ptr2))
474 CLEAR_FLAG (DRCFLAG, (PinType *) b->Connection[0].ptr2);
475 TransferNet (Netl, b, a);
476 /* back up since new subnet is now at old index */
477 n--;
480 /* now add other possible attachment points to the subnet */
481 /* e.g. line end-points and vias */
482 /* don't add non-manhattan lines, the auto-router can't route to them */
483 ALLLINE_LOOP (PCB->Data);
485 if (TEST_FLAG (DRCFLAG, line))
487 conn = GetConnectionMemory (a);
488 conn->X = line->Point1.X;
489 conn->Y = line->Point1.Y;
490 conn->type = LINE_TYPE;
491 conn->ptr1 = layer;
492 conn->ptr2 = line;
493 conn->group = GetLayerGroupNumberByPointer (layer);
494 conn->menu = NULL; /* agnostic view of where it belongs */
495 conn = GetConnectionMemory (a);
496 conn->X = line->Point2.X;
497 conn->Y = line->Point2.Y;
498 conn->type = LINE_TYPE;
499 conn->ptr1 = layer;
500 conn->ptr2 = line;
501 conn->group = GetLayerGroupNumberByPointer (layer);
502 conn->menu = NULL;
505 ENDALL_LOOP;
506 /* add polygons so the auto-router can see them as targets */
507 ALLPOLYGON_LOOP (PCB->Data);
509 if (TEST_FLAG (DRCFLAG, polygon))
511 conn = GetConnectionMemory (a);
512 /* make point on a vertex */
513 conn->X = polygon->Clipped->contours->head.point[0];
514 conn->Y = polygon->Clipped->contours->head.point[1];
515 conn->type = POLYGON_TYPE;
516 conn->ptr1 = layer;
517 conn->ptr2 = polygon;
518 conn->group = GetLayerGroupNumberByPointer (layer);
519 conn->menu = NULL; /* agnostic view of where it belongs */
522 ENDALL_LOOP;
523 VIA_LOOP (PCB->Data);
525 if (TEST_FLAG (DRCFLAG, via))
527 conn = GetConnectionMemory (a);
528 conn->X = via->X;
529 conn->Y = via->Y;
530 conn->type = VIA_TYPE;
531 conn->ptr1 = via;
532 conn->ptr2 = via;
533 conn->group = bottom_group;
536 END_LOOP;
537 if (!NoWarn)
538 Warned |= CheckShorts (a->Connection[0].menu);
540 ClearFlagOnAllObjects (false, DRCFLAG);
541 return (Warned);
544 /* ---------------------------------------------------------------------------
545 * Draw a rat net (tree) having the shortest lines
546 * this also frees the subnet memory as they are consumed
548 * Note that the Netl we are passed is NOT the main netlist - it's the
549 * connectivity for ONE net. It represents the CURRENT connectivity
550 * state for the net, with each Netl->Net[N] representing one
551 * copper-connected subset of the net.
554 static bool
555 DrawShortestRats (NetListType *Netl, void (*funcp) (register ConnectionType *, register ConnectionType *, register RouteStyleType *))
557 RatType *line;
558 register float distance, temp;
559 register ConnectionType *conn1, *conn2, *firstpoint, *secondpoint;
560 PolygonType *polygon;
561 bool changed = false;
562 bool havepoints;
563 Cardinal n, m, j;
564 NetType *next, *subnet, *theSubnet = NULL;
566 /* This is just a sanity check, to make sure we're passed
567 * *something*.
569 if (!Netl || Netl->NetN < 1)
570 return false;
573 * Everything inside the NetList Netl should be connected together.
574 * Each Net in Netl is a group of Connections which are already
575 * connected together somehow, either by real wires or by rats we've
576 * already drawn. Each Connection is a vertex within that blob of
577 * connected items. This loop finds the closest vertex pairs between
578 * each blob and draws rats that merge the blobs until there's just
579 * one big blob.
581 * Just to clarify, with some examples:
583 * Each Netl is one full net from a netlist, like from gnetlist.
584 * Each Netl->Net[N] is a subset of that net that's already
585 * physically connected on the pcb.
587 * So a new design with no traces yet, would have a huge list of Net[N],
588 * each with one pin in it.
590 * A fully routed design would have one Net[N] with all the pins
591 * (for that net) in it.
595 * We keep doing this do/while loop until everything's connected.
596 * I.e. once per rat we add.
598 distance = 0.0;
599 havepoints = true; /* so we run the loop at least once */
600 while (Netl->NetN > 1 && havepoints)
602 /* This is the top of the "find one rat" logic. */
603 havepoints = false;
604 firstpoint = secondpoint = NULL;
606 /* Test Net[0] vs Net[N] for N=1..max. Find the shortest
607 distance between any two points in different blobs. */
608 subnet = &Netl->Net[0];
609 for (j = 1; j < Netl->NetN; j++)
612 * Scan between Net[0] blob (subnet) and Net[N] blob (next).
613 * Note the shortest distance we find.
615 next = &Netl->Net[j];
616 for (n = subnet->ConnectionN - 1; n != -1; n--)
618 conn1 = &subnet->Connection[n];
619 for (m = next->ConnectionN - 1; m != -1; m--)
621 conn2 = &next->Connection[m];
623 * At this point, conn1 and conn2 are two pins in
624 * different blobs of the same net. See how far
625 * apart they are, and if they're "closer" than what
626 * we already have.
630 * Prefer to connect Connections over polygons to the
631 * polygons (ie assume the user wants a via to a plane,
632 * not a daisy chain). Further prefer to pick an existing
633 * via in the Net to make that connection.
635 if (conn1->type == POLYGON_TYPE &&
636 (polygon = (PolygonType *)conn1->ptr2) &&
637 !(distance == 0 &&
638 firstpoint && firstpoint->type == VIA_TYPE) &&
639 IsPointInPolygonIgnoreHoles (conn2->X, conn2->Y, polygon))
641 distance = 0;
642 firstpoint = conn2;
643 secondpoint = conn1;
644 theSubnet = next;
645 havepoints = true;
647 else if (conn2->type == POLYGON_TYPE &&
648 (polygon = (PolygonType *)conn2->ptr2) &&
649 !(distance == 0 &&
650 firstpoint && firstpoint->type == VIA_TYPE) &&
651 IsPointInPolygonIgnoreHoles (conn1->X, conn1->Y, polygon))
653 distance = 0;
654 firstpoint = conn1;
655 secondpoint = conn2;
656 theSubnet = next;
657 havepoints = true;
659 else if ((temp = SQUARE (conn1->X - conn2->X) +
660 SQUARE (conn1->Y - conn2->Y)) < distance || !firstpoint)
662 distance = temp;
663 firstpoint = conn1;
664 secondpoint = conn2;
665 theSubnet = next;
666 havepoints = true;
673 * If HAVEPOINTS is true, we've found a pair of points in two
674 * separate blobs of the net, and need to connect them together.
676 if (havepoints)
678 if (funcp)
680 (*funcp) (firstpoint, secondpoint, subnet->Style);
682 else
684 /* found the shortest distance subnet, draw the rat */
685 if ((line = CreateNewRat (PCB->Data,
686 firstpoint->X, firstpoint->Y,
687 secondpoint->X, secondpoint->Y,
688 firstpoint->group, secondpoint->group,
689 Settings.RatThickness,
690 NoFlags ())) != NULL)
692 if (distance == 0)
693 SET_FLAG (VIAFLAG, line);
694 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
695 DrawRat (line);
696 changed = true;
700 /* copy theSubnet into the current subnet */
701 TransferNet (Netl, theSubnet, subnet);
705 /* presently nothing to do with the new subnet */
706 /* so we throw it away and free the space */
707 FreeNetMemory (&Netl->Net[--(Netl->NetN)]);
708 /* Sadly adding a rat line messes up the sorted arrays in connection finder */
709 /* hace: perhaps not necessarily now that they aren't stored in normal layers */
710 if (changed)
712 FreeConnectionLookupMemory ();
713 InitConnectionLookup ();
715 return (changed);
719 /* ---------------------------------------------------------------------------
720 * AddAllRats puts the rats nest into the layout from the loaded netlist
721 * if SelectedOnly is true, it will only draw rats to selected pins and pads
723 bool
724 AddAllRats (bool SelectedOnly, void (*funcp) (register ConnectionType *, register ConnectionType *, register RouteStyleType *))
726 NetListType *Nets, *Wantlist;
727 NetType *lonesome;
728 ConnectionType *onepin;
729 bool changed, Warned = false;
731 /* the netlist library has the text form
732 * ProcNetlist fills in the Netlist
733 * structure the way the final routing
734 * is supposed to look
736 Wantlist = ProcNetlist (&PCB->NetlistLib);
737 if (!Wantlist)
739 Message (_("Can't add rat lines because no netlist is loaded.\n"));
740 return (false);
742 changed = false;
743 /* initialize finding engine */
744 InitConnectionLookup ();
745 Nets = (NetListType *)calloc (1, sizeof (NetListType));
746 /* now we build another netlist (Nets) for each
747 * net in Wantlist that shows how it actually looks now,
748 * then fill in any missing connections with rat lines.
750 * we first assume each connection is separate
751 * (no routing), then gather them into groups
752 * if the net is all routed, the new netlist (Nets)
753 * will have only one net entry.
754 * Note that DrawShortestRats consumes all nets
755 * from Nets, so *Nets is empty after the
756 * DrawShortestRats call
758 NET_LOOP (Wantlist);
760 CONNECTION_LOOP (net);
762 if (!SelectedOnly
763 || TEST_FLAG (SELECTEDFLAG, (PinType *) connection->ptr2))
765 lonesome = GetNetMemory (Nets);
766 onepin = GetConnectionMemory (lonesome);
767 *onepin = *connection;
768 lonesome->Style = net->Style;
771 END_LOOP;
772 Warned |= GatherSubnets (Nets, SelectedOnly, true);
773 if (Nets->NetN > 0)
774 changed |= DrawShortestRats (Nets, funcp);
776 END_LOOP;
777 FreeNetListMemory (Nets);
778 free (Nets);
779 FreeConnectionLookupMemory ();
780 if (funcp)
781 return (true);
783 if (Warned || changed)
784 Draw ();
786 if (Warned)
787 Settings.RatWarn = true;
789 if (changed)
791 IncrementUndoSerialNumber ();
792 if (PCB->Data->RatN > 0)
794 Message ("%d rat line%s remaining\n", PCB->Data->RatN,
795 PCB->Data->RatN > 1 ? "s" : "");
797 return (true);
799 if (!SelectedOnly && !Warned)
801 if (!PCB->Data->RatN && !badnet)
802 Message (_("Congratulations!!\n"
803 "The layout is complete and has no shorted nets.\n"));
804 else
805 Message (_("Nothing more to add, but there are\n"
806 "either rat-lines in the layout, disabled nets\n"
807 "in the net-list, or missing components\n"));
809 return (false);
812 /* XXX: This is copied in large part from AddAllRats above; for
813 * maintainability, AddAllRats probably wants to be tweaked to use this
814 * version of the code so that we don't have duplication. */
815 NetListListType
816 CollectSubnets (bool SelectedOnly)
818 NetListListType result = { 0, 0, NULL };
819 NetListType *Nets, *Wantlist;
820 NetType *lonesome;
821 ConnectionType *onepin;
823 /* the netlist library has the text form
824 * ProcNetlist fills in the Netlist
825 * structure the way the final routing
826 * is supposed to look
828 Wantlist = ProcNetlist (&PCB->NetlistLib);
829 if (!Wantlist)
831 Message (_("Can't add rat lines because no netlist is loaded.\n"));
832 return result;
834 /* initialize finding engine */
835 InitConnectionLookup ();
836 /* now we build another netlist (Nets) for each
837 * net in Wantlist that shows how it actually looks now,
838 * then fill in any missing connections with rat lines.
840 * we first assume each connection is separate
841 * (no routing), then gather them into groups
842 * if the net is all routed, the new netlist (Nets)
843 * will have only one net entry.
844 * Note that DrawShortestRats consumes all nets
845 * from Nets, so *Nets is empty after the
846 * DrawShortestRats call
848 NET_LOOP (Wantlist);
850 Nets = GetNetListMemory (&result);
851 CONNECTION_LOOP (net);
853 if (!SelectedOnly
854 || TEST_FLAG (SELECTEDFLAG, (PinType *) connection->ptr2))
856 lonesome = GetNetMemory (Nets);
857 onepin = GetConnectionMemory (lonesome);
858 *onepin = *connection;
859 lonesome->Style = net->Style;
862 END_LOOP;
863 /* Note that AndRats is *FALSE* here! */
864 GatherSubnets (Nets, SelectedOnly, false);
866 END_LOOP;
867 FreeConnectionLookupMemory ();
868 return result;
872 * Check to see if a particular name is the name of an already existing rats
873 * line
875 static int
876 rat_used (char *name)
878 if (name == NULL)
879 return -1;
881 MENU_LOOP (&PCB->NetlistLib);
883 if (menu->Name && (strcmp (menu->Name, name) == 0))
884 return 1;
886 END_LOOP;
888 return 0;
891 /* These next two functions moved from the original netlist.c as part of the
892 | gui code separation for the Gtk port.
894 RatType *
895 AddNet (void)
897 static int ratDrawn = 0;
898 char name1[256], *name2;
899 Cardinal group1, group2;
900 char ratname[20];
901 int found;
902 void *ptr1, *ptr2, *ptr3;
903 LibraryMenuType *menu;
904 LibraryEntryType *entry;
906 if (Crosshair.AttachedLine.Point1.X == Crosshair.AttachedLine.Point2.X
907 && Crosshair.AttachedLine.Point1.Y == Crosshair.AttachedLine.Point2.Y)
908 return (NULL);
910 found = SearchObjectByLocation (PAD_TYPE | PIN_TYPE, &ptr1, &ptr2, &ptr3,
911 Crosshair.AttachedLine.Point1.X,
912 Crosshair.AttachedLine.Point1.Y, 5);
913 if (found == NO_TYPE)
915 Message (_("No pad/pin under rat line\n"));
916 return (NULL);
918 if (NAMEONPCB_NAME ((ElementType *) ptr1) == NULL
919 || *NAMEONPCB_NAME ((ElementType *) ptr1) == 0)
921 Message (_("You must name the starting element first\n"));
922 return (NULL);
925 /* will work for pins to since the FLAG is common */
926 group1 = GetLayerGroupNumberBySide (
927 TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ? BOTTOM_SIDE : TOP_SIDE);
928 strcpy (name1, ConnectionName (found, ptr1, ptr2));
929 found = SearchObjectByLocation (PAD_TYPE | PIN_TYPE, &ptr1, &ptr2, &ptr3,
930 Crosshair.AttachedLine.Point2.X,
931 Crosshair.AttachedLine.Point2.Y, 5);
932 if (found == NO_TYPE)
934 Message (_("No pad/pin under rat line\n"));
935 return (NULL);
937 if (NAMEONPCB_NAME ((ElementType *) ptr1) == NULL
938 || *NAMEONPCB_NAME ((ElementType *) ptr1) == 0)
940 Message (_("You must name the ending element first\n"));
941 return (NULL);
943 group2 = GetLayerGroupNumberBySide (
944 TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ? BOTTOM_SIDE : TOP_SIDE);
945 name2 = ConnectionName (found, ptr1, ptr2);
947 menu = netnode_to_netname (name1);
948 if (menu)
950 if (netnode_to_netname (name2))
952 Message (_
953 ("Both connections already in netlist - cannot merge nets\n"));
954 return (NULL);
956 entry = GetLibraryEntryMemory (menu);
957 entry->ListEntry = strdup (name2);
958 netnode_to_netname (name2);
959 goto ratIt;
961 /* ok, the first name did not belong to a net */
962 menu = netnode_to_netname (name2);
963 if (menu)
965 entry = GetLibraryEntryMemory (menu);
966 entry->ListEntry = strdup (name1);
967 netnode_to_netname (name1);
968 goto ratIt;
972 * neither belong to a net, so create a new one.
974 * before creating a new rats here, we need to search
975 * for a unique name.
977 sprintf (ratname, " ratDrawn%i", ++ratDrawn);
978 while (rat_used (ratname))
980 sprintf (ratname, " ratDrawn%i", ++ratDrawn);
983 menu = GetLibraryMenuMemory (&PCB->NetlistLib);
984 menu->Name = strdup (ratname);
985 entry = GetLibraryEntryMemory (menu);
986 entry->ListEntry = strdup (name1);
987 entry = GetLibraryEntryMemory (menu);
988 entry->ListEntry = strdup (name2);
989 menu->flag = 1;
991 ratIt:
992 NetlistChanged (0);
993 return (CreateNewRat (PCB->Data, Crosshair.AttachedLine.Point1.X,
994 Crosshair.AttachedLine.Point1.Y,
995 Crosshair.AttachedLine.Point2.X,
996 Crosshair.AttachedLine.Point2.Y,
997 group1, group2, Settings.RatThickness, NoFlags ()));
1001 char *
1002 ConnectionName (int type, void *ptr1, void *ptr2)
1004 static char name[256];
1005 char *num;
1007 switch (type)
1009 case PIN_TYPE:
1010 num = ((PinType *) ptr2)->Number;
1011 break;
1012 case PAD_TYPE:
1013 num = ((PadType *) ptr2)->Number;
1014 break;
1015 default:
1016 return (NULL);
1018 strcpy (name, UNKNOWN (NAMEONPCB_NAME ((ElementType *) ptr1)));
1019 strcat (name, "-");
1020 strcat (name, UNKNOWN (num));
1021 return (name);