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