1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 ****************************************************************************/
18 /*##########################################################################*
19  *
20  *	Functions in this file are used to adjust wires to make them more
21  *	astheticlly pleasing.  Specifically, kinks in wires are removed and
22  *	directions of wires entering gates are checked.
23  *
24  *
25  *	Major functions in this module are:
26  *
27  *	GWire_snap(GWire*)
28  *	wire_snapgate(GCElement*,int)
29  *##########################################################################*/
30 
31 #include <stdlib.h>
32 
33 #include "tkgate.h"
34 
35 #define crosspoint(x1,x2,x) (((x >= x1) && (x <= x2)) || ((x >= x2) && (x <= x1)))
36 
37 #define vertical(n,m) (((n)->x == (m)->x) && ((n)->y != (m)->y))
38 #define horizontal(n,m) (((n)->x != (m)->x) && ((n)->y == (m)->y))
39 #define zerolength(n,m) (((n)->x == (m)->x) && ((n)->y == (m)->y))
40 
41 /*****************************************************************************
42  *
43  * Snap all of the wires on a pad
44  *
45  * Parameters:
46  *    w			Wire representing the head wire of a pad
47  *
48  *****************************************************************************/
wire_snaplist(GWire * w,int doRedraw)49 static void wire_snaplist(GWire *w,int doRedraw)
50 {
51   for (;w;w = w->next) {
52     if (doRedraw) GWire_draw(w->driver);
53     GWire_snap(w->driver);
54     if (doRedraw) GWire_draw(w->driver);
55   }
56 }
57 
58 /*****************************************************************************
59  *
60  * Force a wire to leave a gate in a specified direction
61  *
62  * Parameters:
63  *    w			Wire to be forced
64  *    d			Direction to force it to.
65  *    retry		This is a retry of an earlier failed attempt
66  *
67  * Returns: 		Magic number.
68  *
69  *****************************************************************************/
wire_force(GWire * w,int d,int retry)70 int wire_force(GWire *w,int d,int retry)
71 {
72   int mod,od;
73   GWireNode *n,*on;
74 
75   mod = 0;
76   n = w->nodes;
77   on = (n->out ? n->out : n->in);
78   if (zerolength(n,on)) {
79     mod = 7;
80     switch (d) {
81     case 0 :
82       wire_move(on,retry ? 5 : 10,0,VERTICAL | HORIZONTAL);
83       break;
84     case 1 :
85       wire_move(on,0,retry ? -5 : -10,VERTICAL | HORIZONTAL);
86       break;
87     case 2 :
88       wire_move(on,retry ? -5 : -10,0,VERTICAL | HORIZONTAL);
89       break;
90     case 3 :
91       wire_move(on,0,retry ? 5 : 10,VERTICAL | HORIZONTAL);
92       break;
93     }
94   }
95   else
96     if (d != wireorient(w->nodes,0)) {
97       mod = 8;
98       n = new_GWireNode();
99       ob_touch(n);
100       n->out = new_GWireNode();
101       ob_touch(n->out);
102       n->out->in = n;
103 
104       n->x = n->out->x = w->nodes->x;
105       n->y = n->out->y = w->nodes->y;
106 
107       if (w->nodes->out) {
108 	n->out->out = w->nodes->out;
109 	n->out->out->in = n->out;
110 	n->in = w->nodes;
111 	ob_touch(n->in);
112 	n->in->out = n;
113 	on = n;
114 	n = n->out;
115 	if (n->out && n->out->out)
116 	  od = wireorient(w->nodes,3);
117 	else
118 	  od = 0;
119       } else {
120 	n->in = w->nodes->in;
121 	ob_touch(n->in);
122 	n->in->out = n;
123 	n->out->out = w->nodes;
124 	ob_touch(w->nodes);
125 	w->nodes->in = n->out;
126 	on = n;
127 	n = n->in;
128 	if (n && n->in)
129 	  od = wireorient(w->nodes,3);
130 	else
131 	  od = 0;
132       }
133 
134       switch (d) {
135       case 0 :
136 	wire_move(on,w->nodes->x + 10 - on->x,0,HORIZONTAL);
137 	if (od == 1)
138 	  wire_move(n,0,retry ? -7 : -15,VERTICAL);
139 	else
140 	  wire_move(n,0,retry ? 7 : 15,VERTICAL);
141 	break;
142       case 1 :
143 	wire_move(on,0,w->nodes->y - 10 - on->y,VERTICAL);
144 	if (od == 2)
145 	  wire_move(n,retry ? -7 : -15,0,HORIZONTAL);
146 	else
147 	  wire_move(n,retry ? 7 : 15,0,HORIZONTAL);
148 	break;
149       case 2 :
150 	wire_move(on,w->nodes->x - 10 - on->x,0,HORIZONTAL);
151 	if (od == 1)
152 	  wire_move(n,0,retry ? -7 : -15,VERTICAL);
153 	else
154 	  wire_move(n,0,retry ? 7 : 15,VERTICAL);
155 	break;
156       case 3 :
157 	wire_move(on,0,w->nodes->y + 10 - on->y,VERTICAL);
158 	if (od == 2)
159 	  wire_move(n,retry ? -7 : -15,0,HORIZONTAL);
160 	else
161 	  wire_move(n,retry ? 7 : 15,0,HORIZONTAL);
162 	break;
163       }
164     }
165 
166   return mod;
167 }
168 
169 /*
170   Does wire_snap on all wires connected to a gate.
171   */
wire_snapgate(GCElement * g,int doRedraw)172 void wire_snapgate(GCElement *g,int doRedraw)
173 {
174   int i;
175   int N = GCElement_numPads(g);
176 
177   for (i = 0;i < N;i++)
178     wire_snaplist(g->wires[i],doRedraw);
179 }
180 
181 /*
182   This function makes sure that the wire end 'w' enters the gate it is
183   connected to from the right direction.  If not, it calls the appropriate
184   function to adjust the wire.
185   */
wire_snapendgate(GWire * w,int * mod,int retry)186 GWireNode *wire_snapendgate(GWire *w,int *mod,int retry)
187 {
188   GCElement *g = w->gate;
189   GGateInfo *gi = g->typeinfo;
190 
191 #if 0
192   printf("wire_snapendgate on %s::%s\n",w->gate->typeinfo->name,w->gate->ename);
193 #endif
194 
195   if (gi->WireSnap)
196     return (*gi->WireSnap)(g,w,mod,retry);
197   else
198     return Generic_wireSnap(g,w,mod,retry);
199 }
200 
201 /*****************************************************************************
202  *
203  * If the destination node is unlabeled, copy label props. from the source.
204  *
205  * Parameters:
206  *      dn 		Destination node
207  *      sn 		Source node
208  *
209  *****************************************************************************/
GWireNode_suggestLabelProps(GWireNode * dn,GWireNode * sn)210 static void GWireNode_suggestLabelProps(GWireNode *dn,GWireNode *sn)
211 {
212   if (!dn) return;
213   if (dn->isLabeled) return;
214   if (!sn->isLabeled) return;
215 
216   dn->isLabeled = 1;
217   dn->labelSide = sn->labelSide;
218   dn->offset = sn->offset;
219 }
220 
221 
222 /*****************************************************************************
223  *
224  * Snaps out short segments of wire with mobile nodes.
225  *
226  * Parameters:
227  *      n			Wire node to snap
228  *      mod
229  *      retry
230  *
231  *****************************************************************************/
wire_snap_aux(GWireNode * n,int mod,int retry)232 static int wire_snap_aux(GWireNode *n,int mod,int retry)
233 {
234   GWireNode *t;
235 
236   if (!n || !n->out)
237     return mod;
238 
239     /* Make sure that wires arround a joint look OK */
240   if (n->end && n->end->gate) {
241     n = wire_snapendgate(n->end,&mod,retry);
242   } else
243     if (n->out->end && n->out->end->gate) {
244       n = wire_snapendgate(n->out->end,&mod,retry);
245       n = n->in;
246     }
247 
248   if (abs(n->x - n->out->x) + abs(n->y - n->out->y) >= SNAPSIZE)
249     return wire_snap_aux(n->out,mod,retry);
250 
251   if (n->out) ob_touch(n->out);
252   if (n->in) ob_touch(n->in);
253 
254 
255   if (n->in && n->out->out && (n->x == n->out->x) && (n->y == n->out->y)) {
256     struct wirenode *nt;
257 
258     nt = n;
259     n->in->out = n->out->out;
260     n->out->out->in = n->in;
261     n = n->in->out;
262     GWireNode_suggestLabelProps(n,nt->out);
263     GWireNode_suggestLabelProps(n,nt);
264     delete_GWireNode(nt->out);
265     delete_GWireNode(nt);
266   } else {
267     if ((!anchoredp(n->out)) && !(n->out->out && anchoredp(n->out->out)) && n->in) {
268       if (!n->out->out) {
269 	n->out->x = n->x;
270 	n->out->y = n->y;
271 	n->in->out = n->out;
272 	n->out->in = n->in;
273 	GWireNode_suggestLabelProps(n->in,n);
274 	delete_GWireNode(n);
275 	return 1;
276       } else {
277 	if (n->in) {
278 	  if ((n->y == n->out->y) && (n->x != n->out->x)) {	/* General case snap */
279 	    ob_touch(n->out->out);
280 	    n->out->out->x = n->x;
281 	  } else {
282 	    if ((n->x == n->out->x) && (n->y != n->out->y)) {
283 	      ob_touch(n->out->out);
284 	      n->out->out->y = n->y;
285 	    }
286 	  }
287 
288 	  n->in->out = n->out->out;
289 	  ob_touch(n->out->out);
290 	  n->out->out->in = n->in;
291 	  t = n;
292 	  n = n->in;
293 	  GWireNode_suggestLabelProps(n,t);
294 	  GWireNode_suggestLabelProps(n,t->out);
295 	  delete_GWireNode(t->out);
296 	  delete_GWireNode(t);
297 	  mod = 2;
298 	} else if (!anchoredp(n)) {
299 	  n->out->end = n->end;
300 	  ob_touch(n->end);
301 	  n->end->nodes = n->out;
302 	  n->out->in = NULL;
303 	  GWireNode_suggestLabelProps(n->in,n);
304 	  delete_GWireNode(n);
305 	  return 3;
306 	}
307       }
308     } else
309       if (n->out->out) {
310 	if (!anchoredp(n) && (n->in ? !anchoredp(n->in) : 0) ) {
311 	  if ((n->in->y == n->y) && (n->in->x != n->x))
312 	    n->in->y = n->out->y;
313 	  else
314 	    if ((n->in->x == n->x) && (n->in->y != n->y))
315 	      n->in->x = n->out->x;
316 	  ob_touch(n->out->out);
317 	  n->in->out = n->out->out;
318 	  n->out->out->in = n->in;
319 	  t = n->in;
320 	  GWireNode_suggestLabelProps(t,n);
321 	  GWireNode_suggestLabelProps(t,n->out);
322 	  delete_GWireNode(n->out);
323 	  delete_GWireNode(n);
324 	  n = t;
325 	  mod = 4;
326 	}  else
327 	  if (!n->in && !anchoredp(n)) {
328 	    ob_touch(n);
329 	    n->x = n->out->x;
330 	    n->y = n->out->y;
331 	    t = n->out;
332 	    n->out = n->out->out;
333 	    ob_touch(n->out);
334 	    n->out->in = n;
335 	    GWireNode_suggestLabelProps(n->in,t);
336 	    delete_GWireNode(t);
337 	    mod = 5;
338 	  }
339       }
340   }
341   return wire_snap_aux(n->out,mod,retry);
342 }
343 
344 /*
345   Return TRUE if the segments [n1 : n1->out] and [n2 : n2->out] are crossed.
346 */
GWireNode_iscrossed(GWireNode * n1,GWireNode * n2)347 static int GWireNode_iscrossed(GWireNode *n1,GWireNode *n2)
348 {
349   if (horizontal(n1,n1->out) && vertical(n2,n2->out))
350     return crosspoint(n1->x,n1->out->x,n2->x) && crosspoint(n2->y,n2->out->y,n1->y);
351   else if (horizontal(n2,n2->out) && vertical(n1,n1->out))
352     return crosspoint(n2->x,n2->out->x,n1->x) && crosspoint(n1->y,n1->out->y,n2->y);
353   else
354     return 0;
355 }
356 
357 /*****************************************************************************
358  *
359  * Remove a loop between node c and node n
360  *
361  * Parameters:
362  *      n		Initial node on the wire to check for loops
363  *
364  *****************************************************************************/
GWireNode_removecross(GWireNode * c,GWireNode * n)365 static void GWireNode_removecross(GWireNode *c,GWireNode *n)
366 {
367   ob_touch(n);
368 
369   if (horizontal(c,c->out))
370     n->y = c->y;
371   else
372     n->x = c->x;
373 
374   ob_touch(n->in);
375   n->in->out = NULL;
376   GWireNode_freenodelist(c->out);
377 
378   ob_touch(c);
379   c->out = n;
380   n->in = c;
381 }
382 
383 /*****************************************************************************
384  *
385  * Detects and removes loops from a wire.
386  *
387  * Parameters:
388  *      n		Initial node on the wire to check for loops
389  *
390  *****************************************************************************/
GWireNode_unloopy(GWireNode * n)391 static int GWireNode_unloopy(GWireNode *n)
392 {
393   GWireNode *c,*start;
394   int removed;
395 
396   removed = 0;
397   start = n;
398   for (n = n->out;n && n->out;n = n->out)
399     for (c = start;c->out != n;c = c->out)
400       if (GWireNode_iscrossed(c,n)) {
401 	GWireNode_removecross(c,n);
402 	removed = 1;
403 	break;
404       }
405 
406   return removed;
407 }
408 
409 /*
410   Try to fix wire up to five times.  If we still can't get it right report
411   what went wrong.
412 */
GWire_snap(GWire * w)413 void GWire_snap(GWire *w)
414 {
415   GWireNode *n;
416   int r[5];
417   int i;
418 
419   for (i = 0;i < 5;i++) {
420     n = w->nodes;
421     GWireNode_unloopy(n);
422     r[i] = wire_snap_aux(w->nodes,0, (i>3));
423     if (r[i] == 0) return;
424   }
425 
426   logError(ERL_ERROR,"Wire Snap failed %d %d %d %d %d in wire_snap.",
427 	   r[0],r[1],r[2],r[3],r[4]);
428 
429 }
430 
431