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