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     One of four main modules pertaining to wires.  Containes functions for moving
20     and drawing wires.  Basic routines to maintain horizontal and vertical wires
21     being moved are defined here.
22 */
23 #include "tkgate.h"
24 
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 
29 #define isgatenode(_n) ((_n)->end && (_n)->end->gate)
30 
31 /*
32  * Straighten out nodes
33  */
wire_netstraight(GWireNode * n,int wt,int ox,int oy,int nx,int ny)34 int wire_netstraight(GWireNode *n,int wt,int ox,int oy,int nx,int ny)
35 {
36   int x,y;
37   GWireNode *next;
38 
39   if (!n) return OK;
40 
41   next = (wt == DRIVER ? n->out : n->in);
42   x = n->x;
43   y = n->y;
44   if (isgatenode(n)) {
45     if ((ox == nx) && (oy == ny)) return OK;
46     if (next && (ox == nx) && next->y == n->y) return OK;
47     if (next && (oy == ny) && next->x == n->x) return OK;
48 
49     return IMMOBILE;
50   }
51 
52   ob_touch(n);
53 
54   switch (wire_netstraight(next,wt,x,y,x+nx-ox,y+ny-oy)) {
55   case OK :
56     n->x += nx - ox;
57     n->y += ny - oy;
58     if ((nx != ox) || (ny != oy)) n->mark = 1;
59     return OK;
60   case IMMOBILE :
61     if ((next->end->orient % 2) == 0) {
62       n->y = next->y;
63       n->x += nx - ox;
64       if (nx - ox) n->mark = 1;
65     } else {
66       n->x = next->x;
67       n->y += ny - oy;
68       if (ny - oy) n->mark = 1;
69     }
70     return OK;
71   }
72   logError(ERL_FATAL,"Unknown case in wire_newstraight.");
73   return IMMOBILE;
74 }
75 
76 /*
77   Splits a node if we can't find a way around it
78  */
wire_splitnode(GWireNode * n,int dir)79 GWireNode *wire_splitnode(GWireNode *n,int dir)
80 {
81   GWireNode *newnode;
82 
83   newnode = new_GWireNode();
84 
85   ob_touch(newnode);
86   newnode->mark = 1;
87   switch (dir) {
88   case OUT_DIR :
89     newnode->out = n->out;
90     newnode->in = n;
91     newnode->end = NULL;
92     if (n->out) {
93       ob_touch(n->out);
94       n->out->in = newnode;
95       newnode->x = (n->x + n->out->x) / 2;
96       newnode->y = (n->y + n->out->y) / 2;
97     } else {
98       newnode->x = n->x;
99       newnode->y = n->y;
100     }
101     ob_touch(n);
102     n->out = newnode;
103     break;
104   case IN_DIR :
105     newnode->in = n->in;
106     newnode->out = n;
107     newnode->end = NULL;
108     if (n->in) {
109       ob_touch(n->in);
110       n->in->out = newnode;
111       newnode->x = (n->x + n->in->x) / 2;
112       newnode->y = (n->y + n->in->y) / 2;
113     } else {
114       newnode->x = n->x;
115       newnode->y = n->y;
116     }
117     ob_touch(n);
118     n->in = newnode;
119     break;
120   }
121   return newnode;
122 }
123 
124 /* Auxilliary function for makestraight */
wire_makestraightaux(GWireNode * n,int wt,int ox,int oy,int nx,int ny)125 void wire_makestraightaux(GWireNode *n,int wt,int ox,int oy,int nx,int ny)
126 {
127   GWireNode *n1,*n2;
128 
129   if (!n) return;
130 
131   if (wire_netstraight(n,wt,ox,oy,nx,ny) == IMMOBILE) {
132     if (wt == DRIVEE && (n->out->out || isgatenode(n->out))) {
133       n1 = wire_splitnode(n,OUT_DIR);
134       n2 = wire_splitnode(n1,OUT_DIR);
135       ob_touch(n1);
136       ob_touch(n2);
137       n2->x = n1->x = (n->x + ox) / 2;
138       n2->y = n1->y = (n->y + oy) / 2;
139     } else if (wt != DRIVEE && (n->in->in || isgatenode(n->in))) {
140       n1 = wire_splitnode(n,IN_DIR);
141       n2 = wire_splitnode(n1,IN_DIR);
142       ob_touch(n1);
143       ob_touch(n2);
144       n2->x = n1->x = (n->x + ox) / 2;
145       n2->y = n1->y = (n->y + oy) / 2;
146     } else {
147       n2 = wire_splitnode(n,wt == DRIVEE ? OUT_DIR : IN_DIR);
148       ob_touch(n2);
149       if (n->x == ox)
150 	n2->y = oy;
151       else
152 	n2->x = ox;
153     }
154     if (wire_netstraight(n2,wt,ox,oy,nx,ny) == IMMOBILE)
155       fprintf(stderr,"Huh? split node didn't help\n");
156   }
157 }
158 
159 /* Takes a moved node and it's old position to figure out how to move the rest of the
160    network to keep the rep. invarient */
wire_makestraight(GWireNode * n,int ox,int oy)161 void wire_makestraight(GWireNode *n,int ox,int oy)
162 {
163   if (n->stype == FULL) {
164     wire_makestraightaux(n->out,DRIVER,ox,oy,n->x,n->y);
165     wire_makestraightaux(n->in,DRIVEE,ox,oy,n->x,n->y);
166   } else {
167     if (n->stype & HORIZONTAL) {
168       ob_touch(n->out);
169       n->out->x += n->x - ox;
170       if (n->x - ox) {
171 	ob_touch(n);
172 	n->mark = 1;
173       }
174     }
175     if (n->stype & VERTICAL) {
176       ob_touch(n->out);
177       n->out->y += n->y - oy;
178       if (n->y - oy) {
179 	ob_touch(n);
180 	n->mark = 1;
181       }
182     }
183   }
184 }
185 
186 /******************************************************************************
187  *
188  * Moves a wire
189  *
190  * Parameters:
191  *      n		Wire node to be moved
192  *      dx,dy		Vector for movement of node
193  *      type		Type of movement (affects movement of connected nodes)
194  *
195  *****************************************************************************/
wire_move(GWireNode * n,int dx,int dy,WireSelectMode_t type)196 void wire_move(GWireNode *n,int dx,int dy,WireSelectMode_t type)
197 {
198   int x,y;
199 
200   ob_touch(n);
201   n->stype = type;
202   x = n->x;
203   y = n->y;
204 
205   if ((type & VERTICAL)) n->y += dy;
206   if ((type & HORIZONTAL)) n->x += dx;
207 
208   if (n->x == x && n->y == y) return;	/* Didn't move */
209 
210   SetModified(MF_NET);
211 
212   if ((type & NOSTRAIGHTEN)) return;
213   wire_makestraight(n,x,y);
214 }
215 
wire_moveto(GWireNode * n,int x,int y)216 void wire_moveto(GWireNode *n,int x,int y)
217 {
218   wire_move(n,x-n->x,y-n->y,FULL);
219 }
220 
221 
222 
wire_hitanynode(int x,int y,GWireList * wires)223 GWireNode *wire_hitanynode(int x,int y,GWireList *wires)
224 {
225   struct wirenode *N,*Close;
226   int CloseD,D;
227 
228   if (!wires) return NULL;
229 
230   CloseD = distance(x,y,wires->wl_wire->nodes->x,wires->wl_wire->nodes->y);
231   Close = wires->wl_wire->nodes;
232 
233   for (;wires;wires = wires->wl_next)
234     if (wires->wl_wire->nodes->out)
235       for (N = wires->wl_wire->nodes;N;N = N->out) {
236 	D = distance(x,y,N->x,N->y);
237 	if (D < CloseD) {
238 	  CloseD = D;
239 	  Close = N;
240 	}
241       }
242 
243   if (CloseD < MAXWIRERANGE)
244     return Close;
245   else
246     return NULL;
247 }
248 
249 /* Test an individual wire's node list */
wire_nodehit(int x,int y,GWireNode * n,int includeend,int range,int corneronly)250 GWireNode *wire_nodehit(int x,int y,GWireNode *n,int includeend,int range,int corneronly)
251 {
252   int testrange;
253   GWireNode *test;
254 
255   if (!n)
256     return NULL;
257 
258   if (includeend || !anchoredp(n))
259     if ((testrange = distance(x,y,n->x,n->y))
260 	< (corneronly ? range : MAXWIRERANGE)) {
261       if ((test = wire_nodehit(x,y,n->out,includeend,testrange,1)))
262 	return test;
263       else {
264 	ob_touch(n);
265 	n->stype = FULL;
266 	return n;
267       }
268     }
269 
270   if ((n->out ? includeend || (!anchoredp(n) && !anchoredp(n->out)) : 0) &&
271       !corneronly) {
272     if ((n->y == n->out->y) &&
273 	(midpointp(x,n->x,n->out->x) && ((testrange = sqr(y - n->y)) < range))) {
274 
275 
276       if ((test = wire_nodehit(x,y,n->out,includeend,testrange,0)))
277 	return test;
278       else {
279 	ob_touch(n);
280 	n->stype = VERTICAL;
281 	return n;
282       }
283     }
284     if ((n->x == n->out->x) &&
285 	(midpointp(y,n->y,n->out->y) && ((testrange = sqr(x - n->x)) < range))) {
286 
287       if ((test = wire_nodehit(x,y,n->out,includeend,testrange,0)))
288 	return test;
289       else {
290 	ob_touch(n);
291 	n->stype = HORIZONTAL;
292 	return n;
293       }
294     }
295   }
296   return wire_nodehit(x,y,n->out,includeend,range,corneronly);
297 }
298 
wire_hitall(int x,int y,GWireList * wires)299 GWireNode *wire_hitall(int x,int y,GWireList *wires)
300 {
301   GWireNode *test;
302 
303   if (!wires)
304     return NULL;
305 
306   if ((wires->wl_wire->nodes->out ? (test =
307       wire_nodehit(x,y,wires->wl_wire->nodes,1,MAXWIRERANGE,0)):NULL))
308     return test;
309   else
310     return wire_hitall(x,y,wires->wl_next);
311 }
312 
313 
314 
315 /* Search for a wire other than 'w' */
wire_hit_other(GWire * w,GWireList * wires)316 GWireNode *wire_hit_other(GWire *w,GWireList *wires)
317 {
318  GWireNode *test;
319 
320  if ((!wires) || (!w))
321    return NULL;
322 
323  if ((wires->wl_wire->nodes->out && (wires->wl_wire->driver != w->driver)) ?
324      (test = wire_nodehit(w->nodes->x,w->nodes->y,wires->wl_wire->nodes,
325 			 1,MAXWIRERANGE,0)):NULL)
326    return test;
327  else
328    return wire_hit_other(w,wires->wl_next);
329 }
330 
331 /* Test to see if theres a wire here */
wire_hit(int x,int y,GWireList * wires)332 GWireNode *wire_hit(int x,int y,GWireList *wires)
333 {
334   GWireNode *test;
335 
336   if (!wires)
337     return NULL;
338 
339   if ((wires->wl_wire->nodes->out ? (test =
340       wire_nodehit(x,y,wires->wl_wire->nodes,0,MAXWIRERANGE,0)):NULL))
341     return test;
342   else
343     return wire_hit(x,y,wires->wl_next);
344 }
345 
wire_iohit(int x,int y,GWireList * wires)346 GWireNode *wire_iohit(int x,int y,GWireList *wires)
347 {
348   GWireNode *test;
349 
350   if (!wires)
351     return NULL;
352 
353 #ifdef DEBUGHIT
354   printf("Wire is: %d\n",wires->wl_wire->wtype);
355 #endif
356   /* Range was 5*5+1 */
357   if ((test = wire_nodehit(x,y,wires->wl_wire->nodes,1,MAXWIRERANGE,0)))
358     /* Danger */
359     return test;
360   else
361     return wire_iohit(x,y,wires->wl_next);
362 }
363 
364 /* Test only at endpoints of a wire. */
wire_endhit(GWire * w,GWireList * wl)365 GWire *wire_endhit(GWire *w,GWireList *wl)
366 {
367   GWire *best_w = 0;
368   int best_d2 = 0;
369 
370   for (;wl;wl = wl->wl_next) {
371     int dx = wl->wl_wire->nodes->x - w->nodes->x;
372     int dy = wl->wl_wire->nodes->y - w->nodes->y;
373     int d2 = dx*dx + dy*dy;
374 
375     if (wl->wl_wire == w || anchoredp(wl->wl_wire->nodes))
376       continue;
377 
378     if (!best_w || d2 < best_d2) {
379       best_w = wl->wl_wire;
380       best_d2 = d2;
381     }
382   }
383 
384   if (best_w && best_d2 < 25)
385     return best_w;
386   else
387     return 0;
388 
389 #if 0
390   if (!wl)
391     return NULL;
392   if ((abs(wl->wl_wire->nodes->x - w->nodes->x) < 5) &&
393       (abs(wl->wl_wire->nodes->y - w->nodes->y) < 5) &&
394       (wl->wl_wire != w) && !anchoredp(wl->wl_wire->nodes))
395     return wl->wl_wire;
396   else
397     return wire_endhit(w,wl->wl_next);
398 #endif
399 }
400 
wire_drivinggate(GWire * w)401 GCElement *wire_drivinggate(GWire *w)
402 {
403   if (!w)
404     return NULL;
405   else
406     return w->driver->gate;
407 }
408 
409 /* Returns the driver of a wire network */
wirenode_driver(GWireNode * n)410 GWire *wirenode_driver(GWireNode *n)
411 {
412   if (!n)
413     return NULL;
414   if (!n->in)
415     return  n->end;
416   else
417     return wirenode_driver(n->in);
418 }
419 
420 /* Returns the drivee of a wire network */
wirenode_drivee(GWireNode * n)421 GWire *wirenode_drivee(GWireNode *n)
422 {
423   if (!n)
424     return NULL;
425   if (!n->out) {
426     return n->end;
427   } else
428     return wirenode_drivee(n->out);
429 }
430 
wire_driver(GWire * w)431 GWire *wire_driver(GWire *w)
432 {
433   return wirenode_driver(w->nodes);
434 }
435 
wire_drivee(GWire * w)436 GWire *wire_drivee(GWire *w)
437 {
438   return wirenode_drivee(w->nodes);
439 }
440 
441 
wire_other(GWire * w)442 GWire *wire_other(GWire *w)
443 {
444   if (w->driver != w)
445     return w->driver;
446   else
447     return wire_drivee(w);
448 }
449 
posongate(GWire * w,GCElement * g,int * p,int * n)450 int posongate(GWire *w,GCElement *g,int *p,int *n)
451 {
452   int i;
453   int N = GCElement_numPads(g);
454 
455   for (i = 0;i < N;i++)
456     if ((*n = findwirepos(w,g->wires[i]))) {
457       *p = i;
458       return 0;
459     }
460 
461   return -1;
462 }
463 
464 /* Searches an i/o wire list for the number of the target wire */
findwirepos(GWire * w,GWire * l)465 int findwirepos(GWire *w,GWire *l)
466 {
467   int p;
468 
469   if (!l)
470     return 0;
471   else {
472     assert(l != l->next);
473     if (w == l)
474       return 1;
475     else
476       if ((p = findwirepos(w,l->next)) != 0)
477 	return p + 1;
478       else
479 	return 0;
480   }
481 }
482 
GetPadPosition(GCElement * g,const char * name)483 int GetPadPosition(GCElement *g,const char *name)
484 {
485   int N = GCElement_numPads(g);
486   int i;
487 
488   for (i = 0;i < N;i++)
489     if (!strcmp(GCElement_getPadName(g,i),name))
490       return i;
491   return -1;
492 }
493 
wire_rePort(GWire * w,int wx,int wy,int dir)494 void wire_rePort(GWire *w,int wx,int wy,int dir)
495 {
496   GWire *ow = wire_other(w);
497 
498   ob_touch(w);
499   wire_move(w->nodes,wx-w->nodes->x,wy-w->nodes->y,VERTICAL|HORIZONTAL);
500 
501   /*
502    * If wire end is not attached, compute mirror image.  Otherwise use brute force
503    * to fix the wire.
504    */
505   if (w->orient != dir) {
506     w->orient = dir;
507     if (!ow->gate) {
508       int cx = w->nodes->x;
509       int cy = w->nodes->y;
510       GWireNode *n;
511 
512 
513       for (n = w->driver->nodes;n;n = n->out) {
514 	ob_touch(n);
515 	//	if (g->orient == 0 || g->orient == 2)
516 	if (dir == D_LEFT || dir == D_RIGHT)
517 	  n->x = cx - (n->x - cx);
518 	else
519 	  n->y = cy - (n->y - cy);
520       }
521     } else
522       GWire_snap(w->driver);
523   }
524 }
525