Revert "Fix locale-dependent gerber output"
[geda-pcb/whiteaudio.git] / src / rats.c
blobd2aa8bb6c8264745531972009440bd7deb29ebd8
1 /* $Id$ */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 Thomas Nau
8 *
9 * This module, rats.c, was written and is Copyright (C) 1997 by harry eaton
10 * this module is also subject to the GNU GPL as described below
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 /* Change History:
29 * Started 6/10/97
30 * Added support for minimum length rat lines 6/13/97
31 * rat lines to nearest line/via 8/29/98
32 * support for netlist window 10/24/98
35 /* rats nest routines
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
41 #include <math.h>
42 #include <stdio.h>
44 #include "global.h"
46 #include "create.h"
47 #include "data.h"
48 #include "draw.h"
49 #include "error.h"
50 #include "file.h"
51 #include "find.h"
52 #include "misc.h"
53 #include "mymem.h"
54 #include "polygon.h"
55 #include "rats.h"
56 #include "search.h"
57 #include "set.h"
58 #include "undo.h"
60 #ifdef HAVE_LIBDMALLOC
61 #include <dmalloc.h>
62 #endif
64 RCSID ("$Id$");
67 #define TRIEDFIRST 0x1
68 #define BESTFOUND 0x2
70 /* ---------------------------------------------------------------------------
71 * some forward declarations
73 static bool FindPad (char *, char *, ConnectionType *, bool);
74 static bool ParseConnection (char *, char *, char *);
75 static bool DrawShortestRats (NetListTypePtr, void (*)(register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr));
76 static bool GatherSubnets (NetListTypePtr, bool, bool);
77 static bool CheckShorts (LibraryMenuTypePtr);
78 static void TransferNet (NetListTypePtr, NetTypePtr, NetTypePtr);
80 /* ---------------------------------------------------------------------------
81 * some local identifiers
83 static bool badnet = false;
84 static Cardinal SLayer, CLayer; /* layer group holding solder/component side */
86 /* ---------------------------------------------------------------------------
87 * parse a connection description from a string
88 * puts the element name in the string and the pin number in
89 * the number. If a valid connection is found, it returns the
90 * number of characters processed from the string, otherwise
91 * it returns 0
93 static bool
94 ParseConnection (char *InString, char *ElementName, char *PinNum)
96 int i, j;
98 /* copy element name portion */
99 for (j = 0; InString[j] != '\0' && InString[j] != '-'; j++)
100 ElementName[j] = InString[j];
101 if (InString[j] == '-')
103 for (i = j; i > 0 && ElementName[i - 1] >= 'a'; i--);
104 ElementName[i] = '\0';
105 for (i = 0, j++; InString[j] != '\0'; i++, j++)
106 PinNum[i] = InString[j];
107 PinNum[i] = '\0';
108 return (false);
110 else
112 ElementName[j] = '\0';
113 Message (_("Bad net-list format encountered near: \"%s\"\n"),
114 ElementName);
115 return (true);
119 /* ---------------------------------------------------------------------------
120 * Find a particular pad from an element name and pin number
122 static bool
123 FindPad (char *ElementName, char *PinNum, ConnectionType * conn, bool Same)
125 ElementTypePtr element;
126 GList *i;
128 if ((element = SearchElementByName (PCB->Data, ElementName)) == NULL)
129 return false;
131 for (i = element->Pad; i != NULL; i = g_list_next (i))
133 PadType *pad = i->data;
135 if (NSTRCMP (PinNum, pad->Number) == 0 &&
136 (!Same || !TEST_FLAG (DRCFLAG, pad)))
138 conn->type = PAD_TYPE;
139 conn->ptr1 = element;
140 conn->ptr2 = pad;
141 conn->group = TEST_FLAG (ONSOLDERFLAG, pad) ? SLayer : CLayer;
143 if (TEST_FLAG (EDGE2FLAG, pad))
145 conn->X = pad->Point2.X;
146 conn->Y = pad->Point2.Y;
148 else
150 conn->X = pad->Point1.X;
151 conn->Y = pad->Point1.Y;
153 return true;
157 for (i = element->Pin; i != NULL; i = g_list_next (i))
159 PinType *pin = i->data;
161 if (!TEST_FLAG (HOLEFLAG, pin) &&
162 pin->Number && NSTRCMP (PinNum, pin->Number) == 0 &&
163 (!Same || !TEST_FLAG (DRCFLAG, pin)))
165 conn->type = PIN_TYPE;
166 conn->ptr1 = element;
167 conn->ptr2 = pin;
168 conn->group = SLayer; /* any layer will do */
169 conn->X = pin->X;
170 conn->Y = pin->Y;
171 return true;
175 return false;
178 /*--------------------------------------------------------------------------
179 * parse a netlist menu entry and locate the corresponding pad
180 * returns true if found, and fills in Connection information
182 bool
183 SeekPad (LibraryEntryType * entry, ConnectionType * conn, bool Same)
185 int j;
186 char ElementName[256];
187 char PinNum[256];
189 if (ParseConnection (entry->ListEntry, ElementName, PinNum))
190 return (false);
191 for (j = 0; PinNum[j] != '\0'; j++);
192 if (j == 0)
194 Message (_("Error! Netlist file is missing pin!\n"
195 "white space after \"%s-\"\n"), ElementName);
196 badnet = true;
198 else
200 if (FindPad (ElementName, PinNum, conn, Same))
201 return (true);
202 if (Same)
203 return (false);
204 if (PinNum[j - 1] < '0' || PinNum[j - 1] > '9')
206 Message ("WARNING! Pin number ending with '%c'"
207 " encountered in netlist file\n"
208 "Probably a bad netlist file format\n", PinNum[j - 1]);
211 Message (_("Can't find %s pin %s called for in netlist.\n"),
212 ElementName, PinNum);
213 return (false);
216 /* ---------------------------------------------------------------------------
217 * Read the library-netlist build a true Netlist structure
220 NetListTypePtr
221 ProcNetlist (LibraryTypePtr net_menu)
223 ConnectionTypePtr connection;
224 ConnectionType LastPoint;
225 NetTypePtr net;
226 static NetListTypePtr Wantlist = NULL;
228 if (!net_menu->MenuN)
229 return (NULL);
230 FreeNetListMemory (Wantlist);
231 free (Wantlist);
232 badnet = false;
234 /* find layer groups of the component side and solder side */
235 SLayer = GetLayerGroupNumberByNumber (solder_silk_layer);
236 CLayer = GetLayerGroupNumberByNumber (component_silk_layer);
238 Wantlist = (NetListTypePtr)calloc (1, sizeof (NetListType));
239 if (Wantlist)
241 ALLPIN_LOOP (PCB->Data);
243 pin->Spare = NULL;
244 CLEAR_FLAG (DRCFLAG, pin);
246 ENDALL_LOOP;
247 ALLPAD_LOOP (PCB->Data);
249 pad->Spare = NULL;
250 CLEAR_FLAG (DRCFLAG, pad);
252 ENDALL_LOOP;
253 MENU_LOOP (net_menu);
255 if (menu->Name[0] == '*' || menu->flag == 0)
257 badnet = true;
258 continue;
260 net = GetNetMemory (Wantlist);
261 if (menu->Style)
263 STYLE_LOOP (PCB);
265 if (style->Name && !NSTRCMP (style->Name, menu->Style))
267 net->Style = style;
268 break;
271 END_LOOP;
273 else /* default to NULL if none found */
274 net->Style = NULL;
275 ENTRY_LOOP (menu);
277 if (SeekPad (entry, &LastPoint, false))
279 if (TEST_FLAG (DRCFLAG, (PinTypePtr) LastPoint.ptr2))
280 Message (_
281 ("Error! Element %s pin %s appears multiple times in the netlist file.\n"),
282 NAMEONPCB_NAME ((ElementTypePtr) LastPoint.ptr1),
283 (LastPoint.type ==
284 PIN_TYPE) ? ((PinTypePtr) LastPoint.ptr2)->
285 Number : ((PadTypePtr) LastPoint.ptr2)->Number);
286 else
288 connection = GetConnectionMemory (net);
289 *connection = LastPoint;
290 /* indicate expect net */
291 connection->menu = menu;
292 /* mark as visited */
293 SET_FLAG (DRCFLAG, (PinTypePtr) LastPoint.ptr2);
294 if (LastPoint.type == PIN_TYPE)
295 ((PinTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
296 else
297 ((PadTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
300 else
301 badnet = true;
302 /* check for more pins with the same number */
303 for (; SeekPad (entry, &LastPoint, true);)
305 connection = GetConnectionMemory (net);
306 *connection = LastPoint;
307 /* indicate expect net */
308 connection->menu = menu;
309 /* mark as visited */
310 SET_FLAG (DRCFLAG, (PinTypePtr) LastPoint.ptr2);
311 if (LastPoint.type == PIN_TYPE)
312 ((PinTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
313 else
314 ((PadTypePtr) LastPoint.ptr2)->Spare = (void *) menu;
317 END_LOOP;
319 END_LOOP;
321 /* clear all visit marks */
322 ALLPIN_LOOP (PCB->Data);
324 CLEAR_FLAG (DRCFLAG, pin);
326 ENDALL_LOOP;
327 ALLPAD_LOOP (PCB->Data);
329 CLEAR_FLAG (DRCFLAG, pad);
331 ENDALL_LOOP;
332 return (Wantlist);
336 * copy all connections from one net into another
337 * and then remove the first net from its netlist
339 static void
340 TransferNet (NetListTypePtr Netl, NetTypePtr SourceNet, NetTypePtr DestNet)
342 ConnectionTypePtr conn;
344 /* It would be worth checking if SourceNet is NULL here to avoid a segfault. Seb James. */
345 CONNECTION_LOOP (SourceNet);
347 conn = GetConnectionMemory (DestNet);
348 *conn = *connection;
350 END_LOOP;
351 DestNet->Style = SourceNet->Style;
352 /* free the connection memory */
353 FreeNetMemory (SourceNet);
354 /* remove SourceNet from its netlist */
355 *SourceNet = Netl->Net[--(Netl->NetN)];
356 /* zero out old garbage */
357 memset (&Netl->Net[Netl->NetN], 0, sizeof (NetType));
360 static bool
361 CheckShorts (LibraryMenuTypePtr theNet)
363 bool newone, warn = false;
364 PointerListTypePtr generic = (PointerListTypePtr)calloc (1, sizeof (PointerListType));
365 /* the first connection was starting point so
366 * the menu is always non-null
368 void **menu = GetPointerMemory (generic);
370 *menu = theNet;
371 ALLPIN_LOOP (PCB->Data);
373 if (TEST_FLAG (DRCFLAG, pin))
375 warn = true;
376 if (!pin->Spare)
378 Message (_("Warning! Net \"%s\" is shorted to %s pin %s\n"),
379 &theNet->Name[2],
380 UNKNOWN (NAMEONPCB_NAME (element)),
381 UNKNOWN (pin->Number));
382 SET_FLAG (WARNFLAG, pin);
383 continue;
385 newone = true;
386 POINTER_LOOP (generic);
388 if (*ptr == pin->Spare)
390 newone = false;
391 break;
394 END_LOOP;
395 if (newone)
397 menu = GetPointerMemory (generic);
398 *menu = pin->Spare;
399 Message (_("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
400 &theNet->Name[2],
401 &((LibraryMenuTypePtr) (pin->Spare))->Name[2]);
402 SET_FLAG (WARNFLAG, pin);
406 ENDALL_LOOP;
407 ALLPAD_LOOP (PCB->Data);
409 if (TEST_FLAG (DRCFLAG, pad))
411 warn = true;
412 if (!pad->Spare)
414 Message (_("Warning! Net \"%s\" is shorted to %s pad %s\n"),
415 &theNet->Name[2],
416 UNKNOWN (NAMEONPCB_NAME (element)),
417 UNKNOWN (pad->Number));
418 SET_FLAG (WARNFLAG, pad);
419 continue;
421 newone = true;
422 POINTER_LOOP (generic);
424 if (*ptr == pad->Spare)
426 newone = false;
427 break;
430 END_LOOP;
431 if (newone)
433 menu = GetPointerMemory (generic);
434 *menu = pad->Spare;
435 Message (_("Warning! Net \"%s\" is shorted to net \"%s\"\n"),
436 &theNet->Name[2],
437 &((LibraryMenuTypePtr) (pad->Spare))->Name[2]);
438 SET_FLAG (WARNFLAG, pad);
442 ENDALL_LOOP;
443 FreePointerListMemory (generic);
444 free (generic);
445 return (warn);
449 /* ---------------------------------------------------------------------------
450 * Determine existing interconnections of the net and gather into sub-nets
452 * initially the netlist has each connection in its own individual net
453 * afterwards there can be many fewer nets with multiple connections each
455 static bool
456 GatherSubnets (NetListTypePtr Netl, bool NoWarn, bool AndRats)
458 NetTypePtr a, b;
459 ConnectionTypePtr conn;
460 Cardinal m, n;
461 bool Warned = false;
463 for (m = 0; Netl->NetN > 0 && m < Netl->NetN; m++)
465 a = &Netl->Net[m];
466 ResetConnections (false);
467 RatFindHook (a->Connection[0].type, a->Connection[0].ptr1,
468 a->Connection[0].ptr2, a->Connection[0].ptr2, false,
469 AndRats);
470 /* now anybody connected to the first point has DRCFLAG set */
471 /* so move those to this subnet */
472 CLEAR_FLAG (DRCFLAG, (PinTypePtr) a->Connection[0].ptr2);
473 for (n = m + 1; n < Netl->NetN; n++)
475 b = &Netl->Net[n];
476 /* There can be only one connection in net b */
477 if (TEST_FLAG (DRCFLAG, (PinTypePtr) b->Connection[0].ptr2))
479 CLEAR_FLAG (DRCFLAG, (PinTypePtr) b->Connection[0].ptr2);
480 TransferNet (Netl, b, a);
481 /* back up since new subnet is now at old index */
482 n--;
485 /* now add other possible attachment points to the subnet */
486 /* e.g. line end-points and vias */
487 /* don't add non-manhattan lines, the auto-router can't route to them */
488 ALLLINE_LOOP (PCB->Data);
490 if (TEST_FLAG (DRCFLAG, line)
491 && ((line->Point1.X == line->Point2.X)
492 || (line->Point1.Y == line->Point2.Y)))
494 conn = GetConnectionMemory (a);
495 conn->X = line->Point1.X;
496 conn->Y = line->Point1.Y;
497 conn->type = LINE_TYPE;
498 conn->ptr1 = layer;
499 conn->ptr2 = line;
500 conn->group = GetLayerGroupNumberByPointer (layer);
501 conn->menu = NULL; /* agnostic view of where it belongs */
502 conn = GetConnectionMemory (a);
503 conn->X = line->Point2.X;
504 conn->Y = line->Point2.Y;
505 conn->type = LINE_TYPE;
506 conn->ptr1 = layer;
507 conn->ptr2 = line;
508 conn->group = GetLayerGroupNumberByPointer (layer);
509 conn->menu = NULL;
512 ENDALL_LOOP;
513 /* add polygons so the auto-router can see them as targets */
514 ALLPOLYGON_LOOP (PCB->Data);
516 if (TEST_FLAG (DRCFLAG, polygon))
518 conn = GetConnectionMemory (a);
519 /* make point on a vertex */
520 conn->X = polygon->Clipped->contours->head.point[0];
521 conn->Y = polygon->Clipped->contours->head.point[1];
522 conn->type = POLYGON_TYPE;
523 conn->ptr1 = layer;
524 conn->ptr2 = polygon;
525 conn->group = GetLayerGroupNumberByPointer (layer);
526 conn->menu = NULL; /* agnostic view of where it belongs */
529 ENDALL_LOOP;
530 VIA_LOOP (PCB->Data);
532 if (TEST_FLAG (DRCFLAG, via))
534 conn = GetConnectionMemory (a);
535 conn->X = via->X;
536 conn->Y = via->Y;
537 conn->type = VIA_TYPE;
538 conn->ptr1 = via;
539 conn->ptr2 = via;
540 conn->group = SLayer;
543 END_LOOP;
544 if (!NoWarn)
545 Warned |= CheckShorts (a->Connection[0].menu);
547 ResetConnections (false);
548 return (Warned);
551 /* ---------------------------------------------------------------------------
552 * Draw a rat net (tree) having the shortest lines
553 * this also frees the subnet memory as they are consumed
555 * Note that the Netl we are passed is NOT the main netlist - it's the
556 * connectivity for ONE net. It represents the CURRENT connectivity
557 * state for the net, with each Netl->Net[N] representing one
558 * copper-connected subset of the net.
561 static bool
562 DrawShortestRats (NetListTypePtr Netl, void (*funcp) (register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr))
564 RatTypePtr line;
565 register float distance, temp;
566 register ConnectionTypePtr conn1, conn2, firstpoint, secondpoint;
567 PolygonTypePtr polygon;
568 bool changed = false;
569 bool havepoints;
570 Cardinal n, m, j;
571 NetTypePtr next, subnet, theSubnet = NULL;
573 /* This is just a sanity check, to make sure we're passed
574 * *something*.
576 if (!Netl || Netl->NetN < 1)
577 return false;
580 * Everything inside the NetList Netl should be connected together.
581 * Each Net in Netl is a group of Connections which are already
582 * connected together somehow, either by real wires or by rats we've
583 * already drawn. Each Connection is a vertex within that blob of
584 * connected items. This loop finds the closest vertex pairs between
585 * each blob and draws rats that merge the blobs until there's just
586 * one big blob.
588 * Just to clarify, with some examples:
590 * Each Netl is one full net from a netlist, like from gnetlist.
591 * Each Netl->Net[N] is a subset of that net that's already
592 * physically connected on the pcb.
594 * So a new design with no traces yet, would have a huge list of Net[N],
595 * each with one pin in it.
597 * A fully routed design would have one Net[N] with all the pins
598 * (for that net) in it.
602 * We keep doing this do/while loop until everything's connected.
603 * I.e. once per rat we add.
605 distance = 0.0;
606 havepoints = true; /* so we run the loop at least once */
607 while (Netl->NetN > 1 && havepoints)
609 /* This is the top of the "find one rat" logic. */
610 havepoints = false;
611 firstpoint = secondpoint = NULL;
613 /* Test Net[0] vs Net[N] for N=1..max. Find the shortest
614 distance between any two points in different blobs. */
615 subnet = &Netl->Net[0];
616 for (j = 1; j < Netl->NetN; j++)
619 * Scan between Net[0] blob (subnet) and Net[N] blob (next).
620 * Note the shortest distance we find.
622 next = &Netl->Net[j];
623 for (n = subnet->ConnectionN - 1; n != -1; n--)
625 conn1 = &subnet->Connection[n];
626 for (m = next->ConnectionN - 1; m != -1; m--)
628 conn2 = &next->Connection[m];
630 * At this point, conn1 and conn2 are two pins in
631 * different blobs of the same net. See how far
632 * apart they are, and if they're "closer" than what
633 * we already have.
637 * Prefer to connect Connections over polygons to the
638 * polygons (ie assume the user wants a via to a plane,
639 * not a daisy chain). Further prefer to pick an existing
640 * via in the Net to make that connection.
642 if (conn1->type == POLYGON_TYPE &&
643 (polygon = (PolygonTypePtr)conn1->ptr2) &&
644 !(distance == 0 &&
645 firstpoint && firstpoint->type == VIA_TYPE) &&
646 IsPointInPolygonIgnoreHoles (conn2->X, conn2->Y, polygon))
648 distance = 0;
649 firstpoint = conn2;
650 secondpoint = conn1;
651 theSubnet = next;
652 havepoints = true;
654 else if (conn2->type == POLYGON_TYPE &&
655 (polygon = (PolygonTypePtr)conn2->ptr2) &&
656 !(distance == 0 &&
657 firstpoint && firstpoint->type == VIA_TYPE) &&
658 IsPointInPolygonIgnoreHoles (conn1->X, conn1->Y, polygon))
660 distance = 0;
661 firstpoint = conn1;
662 secondpoint = conn2;
663 theSubnet = next;
664 havepoints = true;
666 else if ((temp = SQUARE (conn1->X - conn2->X) +
667 SQUARE (conn1->Y - conn2->Y)) < distance || !firstpoint)
669 distance = temp;
670 firstpoint = conn1;
671 secondpoint = conn2;
672 theSubnet = next;
673 havepoints = true;
680 * If HAVEPOINTS is true, we've found a pair of points in two
681 * separate blobs of the net, and need to connect them together.
683 if (havepoints)
685 if (funcp)
687 (*funcp) (firstpoint, secondpoint, subnet->Style);
689 else
691 /* found the shortest distance subnet, draw the rat */
692 if ((line = CreateNewRat (PCB->Data,
693 firstpoint->X, firstpoint->Y,
694 secondpoint->X, secondpoint->Y,
695 firstpoint->group, secondpoint->group,
696 Settings.RatThickness,
697 NoFlags ())) != NULL)
699 if (distance == 0)
700 SET_FLAG (VIAFLAG, line);
701 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
702 DrawRat (line);
703 changed = true;
707 /* copy theSubnet into the current subnet */
708 TransferNet (Netl, theSubnet, subnet);
712 /* presently nothing to do with the new subnet */
713 /* so we throw it away and free the space */
714 FreeNetMemory (&Netl->Net[--(Netl->NetN)]);
715 /* Sadly adding a rat line messes up the sorted arrays in connection finder */
716 /* hace: perhaps not necessarily now that they aren't stored in normal layers */
717 if (changed)
719 FreeConnectionLookupMemory ();
720 InitConnectionLookup ();
722 return (changed);
726 /* ---------------------------------------------------------------------------
727 * AddAllRats puts the rats nest into the layout from the loaded netlist
728 * if SelectedOnly is true, it will only draw rats to selected pins and pads
730 bool
731 AddAllRats (bool SelectedOnly, void (*funcp) (register ConnectionTypePtr, register ConnectionTypePtr, register RouteStyleTypePtr))
733 NetListTypePtr Nets, Wantlist;
734 NetTypePtr lonesome;
735 ConnectionTypePtr onepin;
736 bool changed, Warned = false;
738 /* the netlist library has the text form
739 * ProcNetlist fills in the Netlist
740 * structure the way the final routing
741 * is supposed to look
743 Wantlist = ProcNetlist (&PCB->NetlistLib);
744 if (!Wantlist)
746 Message (_("Can't add rat lines because no netlist is loaded.\n"));
747 return (false);
749 changed = false;
750 /* initialize finding engine */
751 InitConnectionLookup ();
752 SaveFindFlag (DRCFLAG);
753 Nets = (NetListTypePtr)calloc (1, sizeof (NetListType));
754 /* now we build another netlist (Nets) for each
755 * net in Wantlist that shows how it actually looks now,
756 * then fill in any missing connections with rat lines.
758 * we first assume each connection is separate
759 * (no routing), then gather them into groups
760 * if the net is all routed, the new netlist (Nets)
761 * will have only one net entry.
762 * Note that DrawShortestRats consumes all nets
763 * from Nets, so *Nets is empty after the
764 * DrawShortestRats call
766 NET_LOOP (Wantlist);
768 CONNECTION_LOOP (net);
770 if (!SelectedOnly
771 || TEST_FLAG (SELECTEDFLAG, (PinTypePtr) connection->ptr2))
773 lonesome = GetNetMemory (Nets);
774 onepin = GetConnectionMemory (lonesome);
775 *onepin = *connection;
776 lonesome->Style = net->Style;
779 END_LOOP;
780 Warned |= GatherSubnets (Nets, SelectedOnly, true);
781 if (Nets->NetN > 0)
782 changed |= DrawShortestRats (Nets, funcp);
784 END_LOOP;
785 FreeNetListMemory (Nets);
786 free (Nets);
787 FreeConnectionLookupMemory ();
788 RestoreFindFlag ();
789 if (funcp)
790 return (true);
792 if (Warned || changed)
793 Draw ();
795 if (Warned)
796 Settings.RatWarn = true;
798 if (changed)
800 IncrementUndoSerialNumber ();
801 if (PCB->Data->RatN > 0)
803 Message ("%d rat line%s remaining\n", PCB->Data->RatN,
804 PCB->Data->RatN > 1 ? "s" : "");
806 return (true);
808 if (!SelectedOnly && !Warned)
810 if (!PCB->Data->RatN && !badnet)
811 Message (_("Congratulations!!\n"
812 "The layout is complete and has no shorted nets.\n"));
813 else
814 Message (_("Nothing more to add, but there are\n"
815 "either rat-lines in the layout, disabled nets\n"
816 "in the net-list, or missing components\n"));
818 return (false);
821 /* XXX: This is copied in large part from AddAllRats above; for
822 * maintainability, AddAllRats probably wants to be tweaked to use this
823 * version of the code so that we don't have duplication. */
824 NetListListType
825 CollectSubnets (bool SelectedOnly)
827 NetListListType result = { 0, 0, NULL };
828 NetListTypePtr Nets, Wantlist;
829 NetTypePtr lonesome;
830 ConnectionTypePtr onepin;
832 /* the netlist library has the text form
833 * ProcNetlist fills in the Netlist
834 * structure the way the final routing
835 * is supposed to look
837 Wantlist = ProcNetlist (&PCB->NetlistLib);
838 if (!Wantlist)
840 Message (_("Can't add rat lines because no netlist is loaded.\n"));
841 return result;
843 /* initialize finding engine */
844 InitConnectionLookup ();
845 SaveFindFlag (DRCFLAG);
846 /* now we build another netlist (Nets) for each
847 * net in Wantlist that shows how it actually looks now,
848 * then fill in any missing connections with rat lines.
850 * we first assume each connection is separate
851 * (no routing), then gather them into groups
852 * if the net is all routed, the new netlist (Nets)
853 * will have only one net entry.
854 * Note that DrawShortestRats consumes all nets
855 * from Nets, so *Nets is empty after the
856 * DrawShortestRats call
858 NET_LOOP (Wantlist);
860 Nets = GetNetListMemory (&result);
861 CONNECTION_LOOP (net);
863 if (!SelectedOnly
864 || TEST_FLAG (SELECTEDFLAG, (PinTypePtr) connection->ptr2))
866 lonesome = GetNetMemory (Nets);
867 onepin = GetConnectionMemory (lonesome);
868 *onepin = *connection;
869 lonesome->Style = net->Style;
872 END_LOOP;
873 /* Note that AndRats is *FALSE* here! */
874 GatherSubnets (Nets, SelectedOnly, false);
876 END_LOOP;
877 FreeConnectionLookupMemory ();
878 RestoreFindFlag ();
879 return result;
883 * Check to see if a particular name is the name of an already existing rats
884 * line
886 static int
887 rat_used (char *name)
889 if (name == NULL)
890 return -1;
892 MENU_LOOP (&PCB->NetlistLib);
894 if (menu->Name && (strcmp (menu->Name, name) == 0))
895 return 1;
897 END_LOOP;
899 return 0;
902 /* These next two functions moved from the original netlist.c as part of the
903 | gui code separation for the Gtk port.
905 RatTypePtr
906 AddNet (void)
908 static int ratDrawn = 0;
909 char name1[256], *name2;
910 Cardinal group1, group2;
911 char ratname[20];
912 int found;
913 void *ptr1, *ptr2, *ptr3;
914 LibraryMenuTypePtr menu;
915 LibraryEntryTypePtr entry;
917 if (Crosshair.AttachedLine.Point1.X == Crosshair.AttachedLine.Point2.X
918 && Crosshair.AttachedLine.Point1.Y == Crosshair.AttachedLine.Point2.Y)
919 return (NULL);
921 found = SearchObjectByLocation (PAD_TYPE | PIN_TYPE, &ptr1, &ptr2, &ptr3,
922 Crosshair.AttachedLine.Point1.X,
923 Crosshair.AttachedLine.Point1.Y, 5);
924 if (found == NO_TYPE)
926 Message (_("No pad/pin under rat line\n"));
927 return (NULL);
929 if (NAMEONPCB_NAME ((ElementTypePtr) ptr1) == NULL
930 || *NAMEONPCB_NAME ((ElementTypePtr) ptr1) == 0)
932 Message (_("You must name the starting element first\n"));
933 return (NULL);
936 /* will work for pins to since the FLAG is common */
937 group1 = (TEST_FLAG (ONSOLDERFLAG, (PadTypePtr) ptr2) ?
938 GetLayerGroupNumberByNumber (solder_silk_layer) :
939 GetLayerGroupNumberByNumber (component_silk_layer));
940 strcpy (name1, ConnectionName (found, ptr1, ptr2));
941 found = SearchObjectByLocation (PAD_TYPE | PIN_TYPE, &ptr1, &ptr2, &ptr3,
942 Crosshair.AttachedLine.Point2.X,
943 Crosshair.AttachedLine.Point2.Y, 5);
944 if (found == NO_TYPE)
946 Message (_("No pad/pin under rat line\n"));
947 return (NULL);
949 if (NAMEONPCB_NAME ((ElementTypePtr) ptr1) == NULL
950 || *NAMEONPCB_NAME ((ElementTypePtr) ptr1) == 0)
952 Message (_("You must name the ending element first\n"));
953 return (NULL);
955 group2 = (TEST_FLAG (ONSOLDERFLAG, (PadTypePtr) ptr2) ?
956 GetLayerGroupNumberByNumber (solder_silk_layer) :
957 GetLayerGroupNumberByNumber (component_silk_layer));
958 name2 = ConnectionName (found, ptr1, ptr2);
960 menu = netnode_to_netname (name1);
961 if (menu)
963 if (netnode_to_netname (name2))
965 Message (_
966 ("Both connections already in netlist - cannot merge nets\n"));
967 return (NULL);
969 entry = GetLibraryEntryMemory (menu);
970 entry->ListEntry = strdup (name2);
971 netnode_to_netname (name2);
972 goto ratIt;
974 /* ok, the first name did not belong to a net */
975 menu = netnode_to_netname (name2);
976 if (menu)
978 entry = GetLibraryEntryMemory (menu);
979 entry->ListEntry = strdup (name1);
980 netnode_to_netname (name1);
981 goto ratIt;
985 * neither belong to a net, so create a new one.
987 * before creating a new rats here, we need to search
988 * for a unique name.
990 sprintf (ratname, " ratDrawn%i", ++ratDrawn);
991 while (rat_used (ratname))
993 sprintf (ratname, " ratDrawn%i", ++ratDrawn);
996 menu = GetLibraryMenuMemory (&PCB->NetlistLib);
997 menu->Name = strdup (ratname);
998 entry = GetLibraryEntryMemory (menu);
999 entry->ListEntry = strdup (name1);
1000 entry = GetLibraryEntryMemory (menu);
1001 entry->ListEntry = strdup (name2);
1002 menu->flag = 1;
1004 ratIt:
1005 NetlistChanged (0);
1006 return (CreateNewRat (PCB->Data, Crosshair.AttachedLine.Point1.X,
1007 Crosshair.AttachedLine.Point1.Y,
1008 Crosshair.AttachedLine.Point2.X,
1009 Crosshair.AttachedLine.Point2.Y,
1010 group1, group2, Settings.RatThickness, NoFlags ()));
1014 char *
1015 ConnectionName (int type, void *ptr1, void *ptr2)
1017 static char name[256];
1018 char *num;
1020 switch (type)
1022 case PIN_TYPE:
1023 num = ((PinTypePtr) ptr2)->Number;
1024 break;
1025 case PAD_TYPE:
1026 num = ((PadTypePtr) ptr2)->Number;
1027 break;
1028 default:
1029 return (NULL);
1031 strcpy (name, UNKNOWN (NAMEONPCB_NAME ((ElementTypePtr) ptr1)));
1032 strcat (name, "-");
1033 strcat (name, UNKNOWN (num));
1034 return (name);