pcb 4.1.1
An interactive printed circuit board layout editor.

rats.c

Go to the documentation of this file.
00001 
00036 /* Change History:
00037  * Started 6/10/97
00038  * Added support for minimum length rat lines 6/13/97
00039  * rat lines to nearest line/via 8/29/98
00040  * support for netlist window 10/24/98
00041  */
00042 
00043 #ifdef HAVE_CONFIG_H
00044 #include "config.h"
00045 #endif
00046 
00047 #include <math.h>
00048 #include <stdio.h>
00049 
00050 #include "global.h"
00051 
00052 #include "create.h"
00053 #include "data.h"
00054 #include "draw.h"
00055 #include "error.h"
00056 #include "file.h"
00057 #include "find.h"
00058 #include "misc.h"
00059 #include "mymem.h"
00060 #include "polygon.h"
00061 #include "rats.h"
00062 #include "search.h"
00063 #include "set.h"
00064 #include "undo.h"
00065 
00066 #ifdef HAVE_LIBDMALLOC
00067 #include <dmalloc.h>
00068 #endif
00069 
00070 #define TRIEDFIRST 0x1
00071 #define BESTFOUND 0x2
00072 
00073 /* ---------------------------------------------------------------------------
00074  * some forward declarations
00075  */
00076 static bool FindPad (char *, char *, ConnectionType *, bool);
00077 static bool ParseConnection (char *, char *, char *);
00078 static bool DrawShortestRats (NetListType *, void (*)(register ConnectionType *, register ConnectionType *, register RouteStyleType *));
00079 static bool GatherSubnets (NetListType *, bool, bool);
00080 static bool CheckShorts (LibraryMenuType *);
00081 static void TransferNet (NetListType *, NetType *, NetType *);
00082 
00083 /* ---------------------------------------------------------------------------
00084  * some local identifiers
00085  */
00086 static bool badnet = false;
00087 static Cardinal top_group, bottom_group;        /* layer group holding top/bottom side */
00088 
00098 static bool
00099 ParseConnection (char *InString, char *ElementName, char *PinNum)
00100 {
00101   int i, j;
00102 
00103   /* copy element name portion */
00104   for (j = 0; InString[j] != '\0' && InString[j] != '-'; j++)
00105     ElementName[j] = InString[j];
00106   if (InString[j] == '-')
00107     {
00108       for (i = j; i > 0 && ElementName[i - 1] >= 'a'; i--);
00109       ElementName[i] = '\0';
00110       for (i = 0, j++; InString[j] != '\0'; i++, j++)
00111         PinNum[i] = InString[j];
00112       PinNum[i] = '\0';
00113       return (false);
00114     }
00115   else
00116     {
00117       ElementName[j] = '\0';
00118       Message (_("Bad net-list format encountered near: \"%s\"\n"),
00119                ElementName);
00120       return (true);
00121     }
00122 }
00123 
00127 static bool
00128 FindPad (char *ElementName, char *PinNum, ConnectionType * conn, bool Same)
00129 {
00130   ElementType *element;
00131   GList *i;
00132 
00133   if ((element = SearchElementByName (PCB->Data, ElementName)) == NULL)
00134     return false;
00135 
00136   for (i = element->Pad; i != NULL; i = g_list_next (i))
00137     {
00138       PadType *pad = i->data;
00139 
00140       if (NSTRCMP (PinNum, pad->Number) == 0 &&
00141           (!Same || !TEST_FLAG (DRCFLAG, pad)))
00142         {
00143           conn->type = PAD_TYPE;
00144           conn->ptr1 = element;
00145           conn->ptr2 = pad;
00146           conn->group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
00147 
00148           if (TEST_FLAG (EDGE2FLAG, pad))
00149             {
00150               conn->X = pad->Point2.X;
00151               conn->Y = pad->Point2.Y;
00152             }
00153           else
00154             {
00155               conn->X = pad->Point1.X;
00156               conn->Y = pad->Point1.Y;
00157             }
00158           return true;
00159         }
00160     }
00161 
00162   for (i = element->Pin; i != NULL; i = g_list_next (i))
00163     {
00164       PinType *pin = i->data;
00165 
00166       if (!TEST_FLAG (HOLEFLAG, pin) &&
00167           pin->Number && NSTRCMP (PinNum, pin->Number) == 0 &&
00168           (!Same || !TEST_FLAG (DRCFLAG, pin)))
00169         {
00170           conn->type = PIN_TYPE;
00171           conn->ptr1 = element;
00172           conn->ptr2 = pin;
00173           conn->group = bottom_group;        /* any layer will do */
00174           conn->X = pin->X;
00175           conn->Y = pin->Y;
00176           return true;
00177         }
00178     }
00179 
00180   return false;
00181 }
00182 
00188 bool
00189 SeekPad (LibraryEntryType * entry, ConnectionType * conn, bool Same)
00190 {
00191   int j;
00192   char ElementName[256];
00193   char PinNum[256];
00194 
00195   if (ParseConnection (entry->ListEntry, ElementName, PinNum))
00196     return (false);
00197   for (j = 0; PinNum[j] != '\0'; j++);
00198   if (j == 0)
00199     {
00200       Message (_("Error! Netlist file is missing pin!\n"
00201                  "white space after \"%s-\"\n"), ElementName);
00202       badnet = true;
00203     }
00204   else
00205     {
00206       if (FindPad (ElementName, PinNum, conn, Same))
00207         return (true);
00208       if (Same)
00209         return (false);
00210       if (PinNum[j - 1] < '0' || PinNum[j - 1] > '9')
00211         {
00212           Message ("WARNING! Pin number ending with '%c'"
00213                    " encountered in netlist file\n"
00214                    "Probably a bad netlist file format\n", PinNum[j - 1]);
00215         }
00216     }
00217   Message (_("Can't find %s pin %s called for in netlist.\n"),
00218            ElementName, PinNum);
00219   return (false);
00220 }
00221 
00225 NetListType *
00226 ProcNetlist (LibraryType *net_menu)
00227 {
00228   ConnectionType *connection;
00229   ConnectionType LastPoint;
00230   NetType *net;
00231   static NetListType *Wantlist = NULL;
00232 
00233   if (!net_menu->MenuN)
00234     return (NULL);
00235   FreeNetListMemory (Wantlist);
00236   free (Wantlist);
00237   badnet = false;
00238 
00239   /* find layer groups of the component side and solder side */
00240   bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
00241   top_group = GetLayerGroupNumberBySide (TOP_SIDE);
00242 
00243   Wantlist = (NetListType *)calloc (1, sizeof (NetListType));
00244   if (Wantlist)
00245     {
00246       ALLPIN_LOOP (PCB->Data);
00247       {
00248         pin->Spare = NULL;
00249         CLEAR_FLAG (DRCFLAG, pin);
00250       }
00251       ENDALL_LOOP;
00252       ALLPAD_LOOP (PCB->Data);
00253       {
00254         pad->Spare = NULL;
00255         CLEAR_FLAG (DRCFLAG, pad);
00256       }
00257       ENDALL_LOOP;
00258       MENU_LOOP (net_menu);
00259       {
00260         if (menu->Name[0] == '*' || menu->flag == 0)
00261           {
00262             badnet = true;
00263             continue;
00264           }
00265         net = GetNetMemory (Wantlist);
00266         if (menu->Style)
00267           {
00268             STYLE_LOOP (PCB);
00269             {
00270               if (style->Name && !NSTRCMP (style->Name, menu->Style))
00271                 {
00272                   net->Style = style;
00273                   break;
00274                 }
00275             }
00276             END_LOOP;
00277           }
00278         else                    /* default to NULL if none found */
00279           net->Style = NULL;
00280         ENTRY_LOOP (menu);
00281         {
00282           if (SeekPad (entry, &LastPoint, false))
00283             {
00284               if (TEST_FLAG (DRCFLAG, (PinType *) LastPoint.ptr2))
00285                 Message (_
00286                          ("Error! Element %s pin %s appears multiple times in the netlist file.\n"),
00287                          NAMEONPCB_NAME ((ElementType *) LastPoint.ptr1),
00288                          (LastPoint.type ==
00289                           PIN_TYPE) ? ((PinType *) LastPoint.ptr2)->
00290                          Number : ((PadType *) LastPoint.ptr2)->Number);
00291               else
00292                 {
00293                   connection = GetConnectionMemory (net);
00294                   *connection = LastPoint;
00295                   /* indicate expect net */
00296                   connection->menu = menu;
00297                   /* mark as visited */
00298                   SET_FLAG (DRCFLAG, (PinType *) LastPoint.ptr2);
00299                   if (LastPoint.type == PIN_TYPE)
00300                     ((PinType *) LastPoint.ptr2)->Spare = (void *) menu;
00301                   else
00302                     ((PadType *) LastPoint.ptr2)->Spare = (void *) menu;
00303                 }
00304             }
00305           else
00306             badnet = true;
00307           /* check for more pins with the same number */
00308           for (; SeekPad (entry, &LastPoint, true);)
00309             {
00310               connection = GetConnectionMemory (net);
00311               *connection = LastPoint;
00312               /* indicate expect net */
00313               connection->menu = menu;
00314               /* mark as visited */
00315               SET_FLAG (DRCFLAG, (PinType *) LastPoint.ptr2);
00316               if (LastPoint.type == PIN_TYPE)
00317                 ((PinType *) LastPoint.ptr2)->Spare = (void *) menu;
00318               else
00319                 ((PadType *) LastPoint.ptr2)->Spare = (void *) menu;
00320             }
00321         }
00322         END_LOOP;
00323       }
00324       END_LOOP;
00325     }
00326   /* clear all visit marks */
00327   ALLPIN_LOOP (PCB->Data);
00328   {
00329     CLEAR_FLAG (DRCFLAG, pin);
00330   }
00331   ENDALL_LOOP;
00332   ALLPAD_LOOP (PCB->Data);
00333   {
00334     CLEAR_FLAG (DRCFLAG, pad);
00335   }
00336   ENDALL_LOOP;
00337   return (Wantlist);
00338 }
00339 
00344 static void
00345 TransferNet (NetListType *Netl, NetType *SourceNet, NetType *DestNet)
00346 {
00347   ConnectionType *conn;
00348 
00349   /* It would be worth checking if SourceNet is NULL here to avoid a segfault. Seb James. */
00350   CONNECTION_LOOP (SourceNet);
00351   {
00352     conn = GetConnectionMemory (DestNet);
00353     *conn = *connection;
00354   }
00355   END_LOOP;
00356   DestNet->Style = SourceNet->Style;
00357   /* free the connection memory */
00358   FreeNetMemory (SourceNet);
00359   /* remove SourceNet from its netlist */
00360   *SourceNet = Netl->Net[--(Netl->NetN)];
00361   /* zero out old garbage */
00362   memset (&Netl->Net[Netl->NetN], 0, sizeof (NetType));
00363 }
00364 
00365 static bool
00366 CheckShorts (LibraryMenuType *theNet)
00367 {
00368   bool newone, warn = false;
00369   PointerListType *generic = (PointerListType *)calloc (1, sizeof (PointerListType));
00370   /* the first connection was starting point so
00371    * the menu is always non-null
00372    */
00373   void **menu = GetPointerMemory (generic);
00374 
00375   *menu = theNet;
00376   ALLPIN_LOOP (PCB->Data);
00377   {
00378     if (TEST_FLAG (DRCFLAG, pin))
00379       {
00380         warn = true;
00381         if (!pin->Spare)
00382           {
00383             Message (_("Warning! Net \"%s\" is shorted to %s pin %s\n"),
00384                      &theNet->Name[2],
00385                      UNKNOWN (NAMEONPCB_NAME (element)),
00386                      UNKNOWN (pin->Number));
00387             SET_FLAG (WARNFLAG, pin);
00388             continue;
00389           }
00390         newone = true;
00391         POINTER_LOOP (generic);
00392         {
00393           if (*ptr == pin->Spare)
00394             {
00395               newone = false;
00396               break;
00397             }
00398         }
00399         END_LOOP;
00400         if (newone)
00401           {
00402             menu = GetPointerMemory (generic);
00403             *menu = pin->Spare;
00404             Message (_("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
00405                      &theNet->Name[2],
00406                      &((LibraryMenuType *) (pin->Spare))->Name[2]);
00407             SET_FLAG (WARNFLAG, pin);
00408           }
00409       }
00410   }
00411   ENDALL_LOOP;
00412   ALLPAD_LOOP (PCB->Data);
00413   {
00414     if (TEST_FLAG (DRCFLAG, pad))
00415       {
00416         warn = true;
00417         if (!pad->Spare)
00418           {
00419             Message (_("Warning! Net \"%s\" is shorted  to %s pad %s\n"),
00420                      &theNet->Name[2],
00421                      UNKNOWN (NAMEONPCB_NAME (element)),
00422                      UNKNOWN (pad->Number));
00423             SET_FLAG (WARNFLAG, pad);
00424             continue;
00425           }
00426         newone = true;
00427         POINTER_LOOP (generic);
00428         {
00429           if (*ptr == pad->Spare)
00430             {
00431               newone = false;
00432               break;
00433             }
00434         }
00435         END_LOOP;
00436         if (newone)
00437           {
00438             menu = GetPointerMemory (generic);
00439             *menu = pad->Spare;
00440             Message (_("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
00441                      &theNet->Name[2],
00442                      &((LibraryMenuType *) (pad->Spare))->Name[2]);
00443             SET_FLAG (WARNFLAG, pad);
00444           }
00445       }
00446   }
00447   ENDALL_LOOP;
00448   FreePointerListMemory (generic);
00449   free (generic);
00450   return (warn);
00451 }
00452 
00453 
00462 static bool
00463 GatherSubnets (NetListType *Netl, bool NoWarn, bool AndRats)
00464 {
00465   NetType *a, *b;
00466   ConnectionType *conn;
00467   Cardinal m, n;
00468   bool Warned = false;
00469 
00470   for (m = 0; Netl->NetN > 0 && m < Netl->NetN; m++)
00471     {
00472       a = &Netl->Net[m];
00473       ClearFlagOnAllObjects (false, DRCFLAG);
00474       RatFindHook (a->Connection[0].type, a->Connection[0].ptr1,
00475                    a->Connection[0].ptr2, a->Connection[0].ptr2,
00476                    false, DRCFLAG, AndRats);
00477       /* now anybody connected to the first point has DRCFLAG set */
00478       /* so move those to this subnet */
00479       CLEAR_FLAG (DRCFLAG, (PinType *) a->Connection[0].ptr2);
00480       for (n = m + 1; n < Netl->NetN; n++)
00481         {
00482           b = &Netl->Net[n];
00483           /* There can be only one connection in net b */
00484           if (TEST_FLAG (DRCFLAG, (PinType *) b->Connection[0].ptr2))
00485             {
00486               CLEAR_FLAG (DRCFLAG, (PinType *) b->Connection[0].ptr2);
00487               TransferNet (Netl, b, a);
00488               /* back up since new subnet is now at old index */
00489               n--;
00490             }
00491         }
00492       /* now add other possible attachment points to the subnet */
00493       /* e.g. line end-points and vias */
00494       /* don't add non-manhattan lines, the auto-router can't route to them */
00495       ALLLINE_LOOP (PCB->Data);
00496       {
00497         if (TEST_FLAG (DRCFLAG, line))
00498           {
00499             conn = GetConnectionMemory (a);
00500             conn->X = line->Point1.X;
00501             conn->Y = line->Point1.Y;
00502             conn->type = LINE_TYPE;
00503             conn->ptr1 = layer;
00504             conn->ptr2 = line;
00505             conn->group = GetLayerGroupNumberByPointer (layer);
00506             conn->menu = NULL;  /* agnostic view of where it belongs */
00507             conn = GetConnectionMemory (a);
00508             conn->X = line->Point2.X;
00509             conn->Y = line->Point2.Y;
00510             conn->type = LINE_TYPE;
00511             conn->ptr1 = layer;
00512             conn->ptr2 = line;
00513             conn->group = GetLayerGroupNumberByPointer (layer);
00514             conn->menu = NULL;
00515           }
00516       }
00517       ENDALL_LOOP;
00518       /* add polygons so the auto-router can see them as targets */
00519       ALLPOLYGON_LOOP (PCB->Data);
00520       {
00521         if (TEST_FLAG (DRCFLAG, polygon))
00522           {
00523             conn = GetConnectionMemory (a);
00524             /* make point on a vertex */
00525             conn->X = polygon->Clipped->contours->head.point[0];
00526             conn->Y = polygon->Clipped->contours->head.point[1];
00527             conn->type = POLYGON_TYPE;
00528             conn->ptr1 = layer;
00529             conn->ptr2 = polygon;
00530             conn->group = GetLayerGroupNumberByPointer (layer);
00531             conn->menu = NULL;  /* agnostic view of where it belongs */
00532           }
00533       }
00534       ENDALL_LOOP;
00535       VIA_LOOP (PCB->Data);
00536       {
00537         if (TEST_FLAG (DRCFLAG, via))
00538           {
00539             conn = GetConnectionMemory (a);
00540             conn->X = via->X;
00541             conn->Y = via->Y;
00542             conn->type = VIA_TYPE;
00543             conn->ptr1 = via;
00544             conn->ptr2 = via;
00545             conn->group = bottom_group;
00546           }
00547       }
00548       END_LOOP;
00549       if (!NoWarn)
00550         Warned |= CheckShorts (a->Connection[0].menu);
00551     }
00552   ClearFlagOnAllObjects (false, DRCFLAG);
00553   return (Warned);
00554 }
00555 
00590 static bool
00591 DrawShortestRats (NetListType *Netl, void (*funcp) (register ConnectionType *, register ConnectionType *, register RouteStyleType *))
00592 {
00593   RatType *line;
00594   register float distance, temp;
00595   register ConnectionType *conn1, *conn2, *firstpoint, *secondpoint;
00596   PolygonType *polygon;
00597   bool changed = false;
00598   bool havepoints;
00599   Cardinal n, m, j;
00600   NetType *next, *subnet, *theSubnet = NULL;
00601 
00602   /* This is just a sanity check, to make sure we're passed
00603    * *something*.
00604    */
00605   if (!Netl || Netl->NetN < 1)
00606     return false;
00607 
00608   /*
00609    * We keep doing this do/while loop until everything's connected.
00610    * I.e. once per rat we add.
00611    */
00612   distance = 0.0;
00613   havepoints = true; /* so we run the loop at least once */
00614   while (Netl->NetN > 1 && havepoints)
00615     {
00616       /* This is the top of the "find one rat" logic.  */
00617       havepoints = false;
00618       firstpoint = secondpoint = NULL;
00619 
00620       /* Test Net[0] vs Net[N] for N=1..max.  Find the shortest
00621          distance between any two points in different blobs.  */
00622       subnet = &Netl->Net[0];
00623       for (j = 1; j < Netl->NetN; j++)
00624         {
00625           /*
00626            * Scan between Net[0] blob (subnet) and Net[N] blob (next).
00627            * Note the shortest distance we find.
00628            */
00629           next = &Netl->Net[j];
00630           for (n = subnet->ConnectionN - 1; n != -1; n--)
00631             {
00632               conn1 = &subnet->Connection[n];
00633               for (m = next->ConnectionN - 1; m != -1; m--)
00634                 {
00635                   conn2 = &next->Connection[m];
00636                   /*
00637                    * At this point, conn1 and conn2 are two pins in
00638                    * different blobs of the same net.  See how far
00639                    * apart they are, and if they're "closer" than what
00640                    * we already have.
00641                    */
00642 
00643                   /*
00644                    * Prefer to connect Connections over polygons to the
00645                    * polygons (ie assume the user wants a via to a plane,
00646                    * not a daisy chain).  Further prefer to pick an existing
00647                    * via in the Net to make that connection.
00648                    */
00649                   if (conn1->type == POLYGON_TYPE &&
00650                       (polygon = (PolygonType *)conn1->ptr2) &&
00651                       !(distance == 0 &&
00652                         firstpoint && firstpoint->type == VIA_TYPE) &&
00653                       IsPointInPolygonIgnoreHoles (conn2->X, conn2->Y, polygon))
00654                     {
00655                       distance = 0;
00656                       firstpoint = conn2;
00657                       secondpoint = conn1;
00658                       theSubnet = next;
00659                       havepoints = true;
00660                     }
00661                   else if (conn2->type == POLYGON_TYPE &&
00662                       (polygon = (PolygonType *)conn2->ptr2) &&
00663                       !(distance == 0 &&
00664                         firstpoint && firstpoint->type == VIA_TYPE) &&
00665                       IsPointInPolygonIgnoreHoles (conn1->X, conn1->Y, polygon))
00666                     {
00667                       distance = 0;
00668                       firstpoint = conn1;
00669                       secondpoint = conn2;
00670                       theSubnet = next;
00671                       havepoints = true;
00672                     }
00673                   else if ((temp = SQUARE (conn1->X - conn2->X) +
00674                        SQUARE (conn1->Y - conn2->Y)) < distance || !firstpoint)
00675                     {
00676                       distance = temp;
00677                       firstpoint = conn1;
00678                       secondpoint = conn2;
00679                       theSubnet = next;
00680                       havepoints = true;
00681                     }
00682                 }
00683             }
00684         }
00685 
00686       /*
00687        * If HAVEPOINTS is true, we've found a pair of points in two
00688        * separate blobs of the net, and need to connect them together.
00689        */
00690       if (havepoints)
00691         {
00692           if (funcp)
00693             {
00694               (*funcp) (firstpoint, secondpoint, subnet->Style);
00695             }
00696           else
00697             {
00698               /* found the shortest distance subnet, draw the rat */
00699               if ((line = CreateNewRat (PCB->Data,
00700                                         firstpoint->X, firstpoint->Y,
00701                                         secondpoint->X, secondpoint->Y,
00702                                         firstpoint->group, secondpoint->group,
00703                                         Settings.RatThickness,
00704                                         NoFlags ())) != NULL)
00705                 {
00706                   if (distance == 0)
00707                     SET_FLAG (VIAFLAG, line);
00708                   AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
00709                   DrawRat (line);
00710                   changed = true;
00711                 }
00712             }
00713 
00714           /* copy theSubnet into the current subnet */
00715           TransferNet (Netl, theSubnet, subnet);
00716         }
00717     }
00718 
00719   /* presently nothing to do with the new subnet */
00720   /* so we throw it away and free the space */
00721   FreeNetMemory (&Netl->Net[--(Netl->NetN)]);
00722   /* Sadly adding a rat line messes up the sorted arrays in connection finder */
00723   /* hace: perhaps not necessarily now that they aren't stored in normal layers */
00724   if (changed)
00725     {
00726       FreeConnectionLookupMemory ();
00727       InitConnectionLookup ();
00728     }
00729   return (changed);
00730 }
00731 
00732 
00740 bool
00741 AddAllRats (bool SelectedOnly, void (*funcp) (register ConnectionType *, register ConnectionType *, register RouteStyleType *))
00742 {
00743   NetListType *Nets, *Wantlist;
00744   NetType *lonesome;
00745   ConnectionType *onepin;
00746   bool changed, Warned = false;
00747 
00748   /* the netlist library has the text form
00749    * ProcNetlist fills in the Netlist
00750    * structure the way the final routing
00751    * is supposed to look
00752    */
00753   Wantlist = ProcNetlist (&PCB->NetlistLib);
00754   if (!Wantlist)
00755     {
00756       Message (_("Can't add rat lines because no netlist is loaded.\n"));
00757       return (false);
00758     }
00759   changed = false;
00760   /* initialize finding engine */
00761   InitConnectionLookup ();
00762   Nets = (NetListType *)calloc (1, sizeof (NetListType));
00763   /* now we build another netlist (Nets) for each
00764    * net in Wantlist that shows how it actually looks now,
00765    * then fill in any missing connections with rat lines.
00766    *
00767    * we first assume each connection is separate
00768    * (no routing), then gather them into groups
00769    * if the net is all routed, the new netlist (Nets)
00770    * will have only one net entry.
00771    * Note that DrawShortestRats consumes all nets
00772    * from Nets, so *Nets is empty after the
00773    * DrawShortestRats call
00774    */
00775   NET_LOOP (Wantlist);
00776   {
00777     CONNECTION_LOOP (net);
00778     {
00779       if (!SelectedOnly
00780           || TEST_FLAG (SELECTEDFLAG, (PinType *) connection->ptr2))
00781         {
00782           lonesome = GetNetMemory (Nets);
00783           onepin = GetConnectionMemory (lonesome);
00784           *onepin = *connection;
00785           lonesome->Style = net->Style;
00786         }
00787     }
00788     END_LOOP;
00789     Warned |= GatherSubnets (Nets, SelectedOnly, true);
00790     if (Nets->NetN > 0)
00791       changed |= DrawShortestRats (Nets, funcp);
00792   }
00793   END_LOOP;
00794   FreeNetListMemory (Nets);
00795   free (Nets);
00796   FreeConnectionLookupMemory ();
00797   if (funcp)
00798     return (true);
00799 
00800   if (Warned || changed)
00801     Draw ();
00802 
00803   if (Warned)
00804     Settings.RatWarn = true;
00805 
00806   if (changed)
00807     {
00808       IncrementUndoSerialNumber ();
00809       if (PCB->Data->RatN > 0) 
00810         {
00811           Message ("%d rat line%s remaining\n", PCB->Data->RatN,
00812                    PCB->Data->RatN > 1 ? "s" : "");
00813         }
00814       return (true);
00815     }
00816   if (!SelectedOnly && !Warned)
00817     {
00818       if (!PCB->Data->RatN && !badnet)
00819         Message (_("Congratulations!!\n"
00820                    "The layout is complete and has no shorted nets.\n"));
00821       else
00822         Message (_("Nothing more to add, but there are\n"
00823                    "either rat-lines in the layout, disabled nets\n"
00824                    "in the net-list, or missing components\n"));
00825     }
00826   return (false);
00827 }
00828 
00834 NetListListType
00835 CollectSubnets (bool SelectedOnly)
00836 {
00837   NetListListType result = { 0, 0, NULL };
00838   NetListType *Nets, *Wantlist;
00839   NetType *lonesome;
00840   ConnectionType *onepin;
00841 
00842   /* the netlist library has the text form
00843    * ProcNetlist fills in the Netlist
00844    * structure the way the final routing
00845    * is supposed to look
00846    */
00847   Wantlist = ProcNetlist (&PCB->NetlistLib);
00848   if (!Wantlist)
00849     {
00850       Message (_("Can't add rat lines because no netlist is loaded.\n"));
00851       return result;
00852     }
00853   /* initialize finding engine */
00854   InitConnectionLookup ();
00855   /* now we build another netlist (Nets) for each
00856    * net in Wantlist that shows how it actually looks now,
00857    * then fill in any missing connections with rat lines.
00858    *
00859    * we first assume each connection is separate
00860    * (no routing), then gather them into groups
00861    * if the net is all routed, the new netlist (Nets)
00862    * will have only one net entry.
00863    * Note that DrawShortestRats consumes all nets
00864    * from Nets, so *Nets is empty after the
00865    * DrawShortestRats call
00866    */
00867   NET_LOOP (Wantlist);
00868   {
00869     Nets = GetNetListMemory (&result);
00870     CONNECTION_LOOP (net);
00871     {
00872       if (!SelectedOnly
00873           || TEST_FLAG (SELECTEDFLAG, (PinType *) connection->ptr2))
00874         {
00875           lonesome = GetNetMemory (Nets);
00876           onepin = GetConnectionMemory (lonesome);
00877           *onepin = *connection;
00878           lonesome->Style = net->Style;
00879         }
00880     }
00881     END_LOOP;
00882     /* Note that AndRats is *FALSE* here! */
00883     GatherSubnets (Nets, SelectedOnly, false);
00884   }
00885   END_LOOP;
00886   FreeConnectionLookupMemory ();
00887   return result;
00888 }
00889 
00894 static int
00895 rat_used (char *name)
00896 {
00897   if (name == NULL)
00898     return -1;
00899 
00900   MENU_LOOP (&PCB->NetlistLib);
00901   {
00902     if (menu->Name && (strcmp (menu->Name, name) == 0))
00903       return 1;
00904   }
00905   END_LOOP;
00906 
00907   return 0;
00908 }
00909 
00914 RatType *
00915 AddNet (void)
00916 {
00917   static int ratDrawn = 0;
00918   char name1[256], *name2;
00919   Cardinal group1, group2;
00920   char ratname[20];
00921   int found;
00922   void *ptr1, *ptr2, *ptr3;
00923   LibraryMenuType *menu;
00924   LibraryEntryType *entry;
00925 
00926   if (Crosshair.AttachedLine.Point1.X == Crosshair.AttachedLine.Point2.X
00927       && Crosshair.AttachedLine.Point1.Y == Crosshair.AttachedLine.Point2.Y)
00928     return (NULL);
00929 
00930   found = SearchObjectByLocation (PAD_TYPE | PIN_TYPE, &ptr1, &ptr2, &ptr3,
00931                                   Crosshair.AttachedLine.Point1.X,
00932                                   Crosshair.AttachedLine.Point1.Y, 5);
00933   if (found == NO_TYPE)
00934     {
00935       Message (_("No pad/pin under rat line\n"));
00936       return (NULL);
00937     }
00938   if (NAMEONPCB_NAME ((ElementType *) ptr1) == NULL
00939       || *NAMEONPCB_NAME ((ElementType *) ptr1) == 0)
00940     {
00941       Message (_("You must name the starting element first\n"));
00942       return (NULL);
00943     }
00944 
00945   /* will work for pins to since the FLAG is common */
00946   group1 = GetLayerGroupNumberBySide (
00947             TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ? BOTTOM_SIDE : TOP_SIDE);
00948   strcpy (name1, ConnectionName (found, ptr1, ptr2));
00949   found = SearchObjectByLocation (PAD_TYPE | PIN_TYPE, &ptr1, &ptr2, &ptr3,
00950                                   Crosshair.AttachedLine.Point2.X,
00951                                   Crosshair.AttachedLine.Point2.Y, 5);
00952   if (found == NO_TYPE)
00953     {
00954       Message (_("No pad/pin under rat line\n"));
00955       return (NULL);
00956     }
00957   if (NAMEONPCB_NAME ((ElementType *) ptr1) == NULL
00958       || *NAMEONPCB_NAME ((ElementType *) ptr1) == 0)
00959     {
00960       Message (_("You must name the ending element first\n"));
00961       return (NULL);
00962     }
00963   group2 = GetLayerGroupNumberBySide (
00964             TEST_FLAG (ONSOLDERFLAG, (PadType *) ptr2) ? BOTTOM_SIDE : TOP_SIDE);
00965   name2 = ConnectionName (found, ptr1, ptr2);
00966 
00967   menu = netnode_to_netname (name1);
00968   if (menu)
00969     {
00970       if (netnode_to_netname (name2))
00971         {
00972           Message (_
00973                    ("Both connections already in netlist - cannot merge nets\n"));
00974           return (NULL);
00975         }
00976       entry = GetLibraryEntryMemory (menu);
00977       entry->ListEntry = strdup (name2);
00978       netnode_to_netname (name2);
00979       goto ratIt;
00980     }
00981   /* ok, the first name did not belong to a net */
00982   menu = netnode_to_netname (name2);
00983   if (menu)
00984     {
00985       entry = GetLibraryEntryMemory (menu);
00986       entry->ListEntry = strdup (name1);
00987       netnode_to_netname (name1);
00988       goto ratIt;
00989     }
00990 
00991   /*
00992    * neither belong to a net, so create a new one.
00993    *
00994    * before creating a new rats here, we need to search
00995    * for a unique name.
00996    */
00997   sprintf (ratname, "  ratDrawn%i", ++ratDrawn);
00998   while (rat_used (ratname))
00999     {
01000       sprintf (ratname, "  ratDrawn%i", ++ratDrawn);
01001     }
01002 
01003   menu = GetLibraryMenuMemory (&PCB->NetlistLib);
01004   menu->Name = strdup (ratname);
01005   entry = GetLibraryEntryMemory (menu);
01006   entry->ListEntry = strdup (name1);
01007   entry = GetLibraryEntryMemory (menu);
01008   entry->ListEntry = strdup (name2);
01009   menu->flag = 1;
01010 
01011 ratIt:
01012   NetlistChanged (0);
01013   return (CreateNewRat (PCB->Data, Crosshair.AttachedLine.Point1.X,
01014                         Crosshair.AttachedLine.Point1.Y,
01015                         Crosshair.AttachedLine.Point2.X,
01016                         Crosshair.AttachedLine.Point2.Y,
01017                         group1, group2, Settings.RatThickness, NoFlags ()));
01018 }
01019 
01024 char *
01025 ConnectionName (int type, void *ptr1, void *ptr2)
01026 {
01027   static char name[256];
01028   char *num;
01029 
01030   switch (type)
01031     {
01032     case PIN_TYPE:
01033       num = ((PinType *) ptr2)->Number;
01034       break;
01035     case PAD_TYPE:
01036       num = ((PadType *) ptr2)->Number;
01037       break;
01038     default:
01039       return (NULL);
01040     }
01041   strcpy (name, UNKNOWN (NAMEONPCB_NAME ((ElementType *) ptr1)));
01042   strcat (name, "-");
01043   strcat (name, UNKNOWN (num));
01044   return (name);
01045 }