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     Last edit by hansen on Mon Jan 19 18:16:15 2009
19 ****************************************************************************/
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include "tkgate.h"
24 
25 #define JOINT_RIGHT 0
26 #define JOINT_TOP 1
27 #define JOINT_LEFT 2
28 #define JOINT_BOTTOM 3
29 
30 void GWireNode_setends(GWireNode *n);
31 
32 GCElement *Joint_Make(EditState **es,GModuleDef *M,int GType,
33 		      int x,int y,int r,const char *Name,int noWires,const char **options,int nOptions);
34 
35 void Joint_Draw(GCElement *g,int md);
36 
37 void Joint_Delete(GCElement *g,GModuleDef *M,int drawp);
38 void Joint_Init(GCElement *g);
39 void Joint_VerSave(FILE *f,GCElement *g);
40 void Joint_Rotate(GCElement *g, int centX, int centY, int rdir);
41 GWireNode *Joint_wireSnap(GCElement *g,GWire *w,int *mod,int retry);
42 
43 static iconDimensions joint_iconDims[] = {
44   {0, 0, 3, 3, 1, 1},
45 };
46 static int joint_iconBoldOffset = 4;
47 
48 static char *psJoint[] = {
49   "%",
50   "% A Solder Joint",
51   "%",
52   "/joint {",
53   "  startgate",
54   "  0 0 2 0 360 arc",
55   "  closepath",
56   "  fill",
57   "  grestore",
58   "} bind def",
59   0
60 };
61 
62 GPadLoc joint_L_loc[] = {
63   {-1,0,-1,0,D_LEFT}
64 };
65 
66 GPadLoc joint_R_loc[] = {
67   {1,0,1,0,D_RIGHT}
68 };
69 
70 GPadLoc joint_T_loc[] = {
71   {0,-1,0,-1,D_UP}
72 };
73 
74 GPadLoc joint_B_loc[] = {
75   {0,1,0,1,D_DOWN}
76 };
77 
78 
79 GGateInfo gate_joint_info = {
80   GC_JOINT,
81   "JOINT",
82   "joint",0x0,
83   "joint",psJoint,
84   -1,-1,
85 
86   {{0}},
87   joint_iconDims,
88 
89   /*
90      The order here is critical (only for joints).  It should correspond
91      to the direction of the pin.  This kludge is only for this type.
92      */
93   4,{{"R",ANY,0,0,joint_R_loc,0},{"T",ANY,0,0,joint_T_loc,0},
94        {"L",ANY,0,0,joint_L_loc,0},{"B",ANY,0,0,joint_B_loc,0}},
95   {{0,0,CT},{0,0,CT},{0,0,CT},{0,0,CT}},
96   {0,1},
97 
98   {0},
99 
100   Joint_Make,
101   Nop_WriteCellDef,
102   Joint_Init,
103   Joint_Delete,
104   Generic_GetExtents,
105   Generic_HitDistance,
106   Joint_Draw,
107   Generic_Move,
108   Generic_Copy,
109   Err_AddInput,
110   Err_AddOutput,
111   Err_AddInOut,
112   Joint_Rotate,
113   Err_RemovePort,
114   Err_ChangePin,
115   Nop_SimInitFunc,
116   Nop_SimHitFunc,
117   Generic_PSWrite,
118   Generic_EditProps,
119   Joint_VerSave,
120   0,
121   0,
122   Joint_wireSnap
123 };
124 
125 
Joint_Make(EditState ** es,GModuleDef * M,int GType,int x,int y,int r,const char * Name,int noWires,const char ** options,int nOptions)126 GCElement *Joint_Make(EditState **es,GModuleDef *M,int GType,
127 		      int x,int y,int r,const char *Name,int noWires,const char **options,int nOptions)
128 {
129   GCElement *g = Generic_Make(es,M,GType,x,y,r,Name,noWires,options,nOptions);
130 
131   ob_touch(g);
132   g->show_name = 0;
133 
134   return g;
135 }
136 
Joint_Init(GCElement * g)137 void Joint_Init(GCElement *g)
138 {
139   ob_touch(g);
140   g->u.joint.driver = -1;
141 }
142 
Joint_Delete(GCElement * g,GModuleDef * M,int drawp)143 void Joint_Delete(GCElement *g,GModuleDef *M,int drawp)
144 {
145 }
146 
Joint_Draw(GCElement * g,int md)147 void Joint_Draw(GCElement *g,int md)
148 {
149   mk_gate(g->xpos,g->ypos,g->typeinfo,g->orient,g->selected);
150   gate_drawWires(g,md);
151 }
152 
153 
154 /**********************************************************************
155  *
156  *  Special joint handling in wires follows....
157  *
158  **********************************************************************/
159 
160 extern int debugmode;
161 
162 /*
163    Return the position of the free position on a joint, or
164    -1 of there are no free positions.
165 */
joint_free(GCElement * g)166 int joint_free(GCElement *g)
167 {
168   int i;
169 
170   for (i = 0;i < 4;i++)
171     if (!g->wires[i])
172       return i;
173 
174   return -1;
175 }
176 
177 /*
178    Return any wire of a joint, or NULL if there are none.
179 */
joint_any(GCElement * g)180 GWire *joint_any(GCElement *g)
181 {
182  int i;
183 
184   for (i = 0;i < 4;i++)
185     if (g->wires[i])
186       return g->wires[i];
187  return 0;
188 }
189 
190 
191 /*
192    Connect wire at node n to joint g.  It is assumed that the
193    wires have been erased and are not currently visible.
194 */
joint_connect(GCElement * g,GWireNode * n)195 void joint_connect(GCElement *g,GWireNode *n)
196 {
197   GWireNode *nn;
198   int i,dx,dy;
199   GWire *w,*jw,*o_w;
200 
201   message(0,"trying joint_connect.");
202   if (n->in) {
203     nn = n->in;
204     if (g->u.joint.driver >= 0) {
205       if (n->end) {
206 	join_treereverse(n->end);
207 	nn = n->out;
208       } else {
209 	message(0,"Connection refused because of bogosity.");
210 	return;
211       }
212     }
213   } else
214     nn = n->out;
215 
216   w = wirenode_driver(n);
217   jw = joint_any(g);
218 
219   if (!net_connectOK(w->net,jw->net,0)) return;
220 
221   if ((i = joint_free(g)) < 0)
222     return;
223 
224   dx = g->xpos - n->x;
225   dy = g->ypos - n->y;
226 
227   ob_touch(n);
228   ob_touch(n->end);
229   if (n->x == nn->x) {
230     if (n->y > nn->y) {
231       dy -= 2;
232       n->end->orient = RIGHT;
233     } else {
234       dy += 2;
235       n->end->orient = LEFT;
236     }
237   } else {
238     if (n->x > nn->x) {
239       dx -= 2;
240       n->end->orient = DOWN;
241     } else {
242       dx += 2;
243       n->end->orient = UP;
244     }
245   }
246 
247   if (n->end && (o_w = wire_other(n->end)) && !o_w->gate) {
248     n->x += dx;
249     n->y += dy;
250   } else
251     wire_move(n,dx,dy,n->stype);
252 
253   ob_touch(g);
254   ob_touch(n->end);
255   g->wires[i] = n->end;
256   n->end->gate = g;
257   if (n->in)
258     g->u.joint.driver = i;
259 }
260 
261 /*
262    Create a new joint to merge wires w1 through w4.
263 */
joint_make(int x,int y,GWire * w1,GWire * w2,GWire * w3,GWire * w4,EditState * es)264 void joint_make(int x,int y,GWire *w1,GWire *w2,GWire *w3,GWire *w4,EditState *es)
265 {
266   GCElement *j;
267   int i;
268 
269   j = gate_new(x,y,0,GC_JOINT);
270   ob_touch(j);
271   j->wires[0] = w1;
272   j->wires[1] = w2;
273   j->wires[2] = w3;
274   j->wires[3] = w4;
275 
276   for (i = 0;i < 4;i++) {
277     GWire *lw = j->wires[i];
278     if (lw) {
279       ob_touch(lw);
280       lw->gate = j;
281       if (lw->nodes->in)
282 	j->u.joint.driver = i;
283     }
284   }
285 
286   gate_add(es->env,j);
287 }
288 
289 /*
290     Returns the orientation of a wire on a joint.
291 */
joint_wiredir(GWire * w)292 int joint_wiredir(GWire *w)
293 {
294   if (w->nodes->x > w->gate->xpos)
295     return JOINT_RIGHT;
296   else if (w->nodes->x < w->gate->xpos)
297     return JOINT_LEFT;
298   else if (w->nodes->y < w->gate->ypos)
299     return JOINT_TOP;
300   else
301     return JOINT_BOTTOM;
302 }
303 
joint_fixwires_aux(GCElement * j,GWireNode * n,int td)304 static void joint_fixwires_aux(GCElement *j,GWireNode *n,int td)
305 {
306   int x,y,nx,ny;
307 
308   nx = ny = 0;
309   x = n->x;
310   y = n->y;
311 
312   ob_touch(n->end);
313   switch (td) {
314   case JOINT_RIGHT :	/* --> */
315     ny = j->ypos;
316     nx = j->xpos + 2;
317     n->end->orient = UP;    /* These directions are confusing, but correct */
318     break;
319   case JOINT_TOP :	/*  ^  */
320     ny = j->ypos - 2;
321     nx = j->xpos;
322     n->end->orient = RIGHT;
323     break;
324   case JOINT_LEFT :	/* <-- */
325     ny = j->ypos;
326     nx = j->xpos - 2;
327     n->end->orient = DOWN;
328     break;
329   case JOINT_BOTTOM :	/*  v  */
330     ny = j->ypos + 2;
331     nx = j->xpos;
332     n->end->orient = LEFT;
333     break;
334   }
335 
336   if (td != -1) {
337     wire_move(n,nx-x,ny-y,VERTICAL | HORIZONTAL);
338   }
339 }
340 
341 /*
342   Correct the wire so that the end enters the joint in direction 'd'.
343 
344   0:
345   |
346 #--+
347 
348   1:
349   +----
350   |
351 #
352 
353   2:
354 
355   +---#
356   |
357 
358   3:
359 
360 #
361   |
362   +---
363   */
joint_correct(GWireNode * n,int d)364 static void joint_correct(GWireNode *n,int d)
365 {
366   GWireNode *nn;
367 
368   nn = new_GWireNode();
369   ob_touch(n);
370   ob_touch(nn);
371 
372   nn->x = n->x;
373   nn->y = n->y;
374 
375   switch (d) {
376   case 0 :
377     wire_move(n,10,0,VERTICAL | HORIZONTAL);
378     break;
379   case 1 :
380     wire_move(n,0,-10,VERTICAL | HORIZONTAL);
381     break;
382   case 2 :
383     wire_move(n,-10,0,VERTICAL | HORIZONTAL);
384     break;
385   case 3 :
386     wire_move(n,0,10,VERTICAL | HORIZONTAL);
387     break;
388   }
389 
390   if (n->out) {
391     n->in = nn;
392     nn->out = n;
393   } else {
394     n->out = nn;
395     nn->in = n;
396   }
397   nn->end = n->end;
398   n->end = NULL;
399 
400   ob_touch(nn->end);
401   nn->end->nodes = nn;
402 }
403 
404 /*
405   Fixes the coordinates of wire 'w' arround the joint 'j'.  If two wires want to be on
406   the same position arround the joint, we need to edit the wires until they don't.
407 
408        1
409        |
410    2 --+-- 0
411        |
412        3
413 
414 */
415 #if 1
joint_fixwires(GCElement * j,GWire * w,int retry)416 void joint_fixwires(GCElement *j,GWire *w,int retry)
417 {
418   int i,k,td;
419   GWireNode *n;
420   int pos[4];
421   GWire *wa[4];
422 
423   while ((td = wireorient(w->nodes,0)) == -1) {
424     if (debugmode) printf("joint_fixwires() Removing zero node\n");
425     if (w->nodes->in ? !w->nodes->in->in : !w->nodes->out->out) {
426       if (debugmode) printf("Punting fixwires!\n");
427       return;
428     }
429     wire_deletenode(w->nodes);
430   }
431 
432   for (i = 0;i < 4;i++) pos[i] = 0;	/* Flag all positions as unused */
433 
434   for (i = 0;i < 4;i++)
435     if (j->wires[i] && ((n = j->wires[i]->nodes) != w->nodes)) {
436       td = wireorient(n,0);
437       joint_fixwires_aux(j,n,td);
438       if (td == -1) {
439 	td = joint_wiredir(n->end);
440       }
441       pos[td] = 1;
442     }
443 
444   td = wireorient(w->nodes,0);
445   if (td == -1) {
446     logError(ERL_WARN,"Weird place for 0-wire 1 in joint_fixwires.");
447     return;
448   }
449 
450   if (pos[td]) {
451     td = wireorient(w->nodes,1);
452     if (td == -1) {
453       td = (wireorient(w->nodes,0) + 1) & 3;
454     }
455     if (!pos[td])
456       joint_correct(w->nodes,td);
457     else {
458       td = (wireorient(w->nodes,0) + 2) & 3;
459       if (pos[td])
460 	joint_correct(w->nodes,(((td = wireorient(w->nodes,1))+2)&3));
461       else {
462 	joint_fixwires_aux(j,w->nodes,td);
463 	wire_force(w,td,retry);
464       }
465     }
466   }
467   if (debugmode) printf("Fixing direction %d\n",td);
468   joint_fixwires_aux(j,w->nodes,td);
469 
470   ob_touch(j);
471   for (i = 0;i < 4;i++) {
472     wa[i] = j->wires[i];
473     j->wires[i] = 0;
474   }
475 
476   for (i = 0;i < 4;i++) {
477     if (!wa[i]) continue;
478 
479     td = wireorient(wa[i]->nodes,0);
480     if (td < 0 || j->wires[td]) {
481       for (k = 0;k < 4;k++)
482 	if (!j->wires[k]) {
483 	  td = k;
484 	  break;
485 	}
486     }
487 
488     ob_touch(j);
489     ob_touch(wa[i]);
490     j->wires[td] = wa[i];
491     wa[i]->orient = j->typeinfo->Pad[td].Loc[0].dir;
492   }
493 }
494 #endif
495 
496 /*
497     Unhooks n from a joint and possibly deletes the joint.
498 */
joint_dejoint(GWireNode * n,GModuleDef * M,int drawp)499 int joint_dejoint(GWireNode *n,GModuleDef *M,int drawp)
500 {
501   struct celemnt *j;
502   struct wire *wi,*wo;
503   int num,i,k;
504 
505   k = 0;
506 
507   j = n->end->gate;
508   if ((!j) || (j->typeinfo->code != GC_JOINT))
509     logError(ERL_FATAL,"Called on non-joint in joint_dejoint.");
510 
511   for (i = 0,num = 0;i < 4;i++)
512     if (j->wires[i]) {
513       num++;
514       if (j->wires[i]->nodes == n) k = i;
515     }
516 
517   switch (num) {
518   case 0 :
519   case 1 :
520   case 2 :
521     logError(ERL_WARN,"Joint with too few wires in joint_dejoint.");
522     return 0;
523   case 3 :
524     wi = wo = NULL;
525     for (i = 0;i < 4;i++) {
526       if (j->wires[i] && j->wires[i]->nodes !=n) {
527 	if (wo) {
528 	  wi = j->wires[i];
529 	  if (drawp) GWire_draw(wi->driver);
530 	  ob_touch(wi);
531 	  wi->gate = NULL;
532 	} else {
533 	  wo = j->wires[i];
534 	  if (drawp) GWire_draw(wo->driver);
535 	  ob_touch(wo);
536 	  wo->gate = NULL;
537 	}
538       }
539       ob_touch(j);
540       j->wires[i] = NULL;
541     }
542     if (!(wi && wo))
543       logError(ERL_FATAL,"Bad formation in joint_dejoint.");
544 
545     if (wi->driver->gate||wire_drivee(wi)->gate||
546 	wo->driver->gate||wire_drivee(wo)->gate) {
547       GWire *rw;
548 
549       ob_touch(wi->nodes);
550       ob_touch(wo->nodes);
551       wi->nodes->x = j->xpos;
552       wi->nodes->y = j->ypos;
553       wo->nodes->x = j->xpos;
554       wo->nodes->y = j->ypos;
555       rw = wire_connect(M,wi,wo);
556       if (rw && drawp) GWire_draw(rw->driver);
557     } else
558       if (!(wire_nuke(wi,0,M)&&wire_nuke(wo,0,M)))
559 	logError(ERL_FATAL,"Wires can't be nuked in joint_dejoint.");
560 
561     if (M) M->m_wires = wire_unlink(M->m_wires,n->end);
562     wire_free(n->end);
563     delete_GWireNode(n);
564 
565     if (drawp) gate_draw(j,0);
566     gate_remove(M,j);
567     ob_free(j);
568 
569     return 1;
570   case 4 :
571     if (j->wires[k]->driver != j->wires[k]) {
572       join_treereverse_aux(j->wires[((k+1)%4)]);
573     }
574 
575     ob_touch(j);
576     j->wires[k] = NULL;
577 
578     wire_finalizeNet(j->wires[((k+1)%4)]);
579 
580     if (M) {
581       ob_touch(M);
582       M->m_wires = wire_unlink(M->m_wires,n->end);
583     }
584     wire_free(n->end);
585     delete_GWireNode(n);
586 
587     return 1;
588   }
589   return 0;
590 }
591 
592 /*
593   A wire leading to a splice has been cut, we need to decide if we should
594   nuke this splice or not.  A splice will be nuked if all other wires leading to
595   it are not attached to anything.
596 */
joint_desplice(GWireNode * n,GModuleDef * M)597 int joint_desplice(GWireNode *n,GModuleDef *M)
598 {
599   GCElement *g;
600   GWire *w1,*w2,*we1,*we2;
601 
602   g = n->end->gate;
603   switch (g->typeinfo->code) {
604   case GC_TAP :
605     if (n->end == g->wires[TAP_IN]) {
606       w1 = g->wires[TAP_OUT];
607       w2 = g->wires[TAP_TAP];
608       we1 = wire_drivee(w1);
609       we2 = wire_drivee(w2);
610     } else if (n->end == g->wires[TAP_OUT]) {
611       w1 = g->wires[TAP_IN];
612       w2 = g->wires[TAP_TAP];
613       we1 = w1->driver;
614       we2 = wire_drivee(w2);
615     } else if (n->end == g->wires[TAP_TAP]) {
616       w1 = g->wires[TAP_OUT];
617       w2 = g->wires[TAP_IN];
618       we1 = wire_drivee(w1);
619       we2 = w2->driver;
620     } else {
621       logError(ERL_WARN,"Wire not found on splice in joint_desplice.");
622       return 0;
623     }
624     if ((!we1->gate)&&(!we2->gate)) {
625       int N = GCElement_numPads(g);
626       int i;
627 
628       gate_unattachwirelist(w1,M,1);
629       gate_unattachwirelist(w2,M,1);
630 
631       ob_touch(g);
632       for (i = 0;i < N;i++)
633 	g->wires[i] = NULL;
634       gate_draw(g,0);
635       gate_remove(M,g);
636       ob_free(g);
637       ob_touch(M);
638       M->m_wires = wire_unlink(M->m_wires,n->end);
639       wire_free(n->end);
640       delete_GWireNode(n);
641       return 1;
642     }
643     return 0;
644   default :
645     logError(ERL_WARN,"Unknown splice element found in joint_desplice.");
646     return 0;
647   }
648 }
649 
Joint_VerSave(FILE * f,GCElement * g)650 void Joint_VerSave(FILE *f,GCElement *g)
651 {
652   int i;
653   GWire *w;
654   int N = GCElement_numPads(g);
655 
656   w = 0;
657 
658   /*
659      Gate any pin on the joint.
660    */
661   for (i = 0;i < N;i++)
662     if ((w = g->wires[i])) break;
663 
664   fprintf(f,"  //: joint %s (%s) @(%d, %d)",g->ename,w->net->n_signame,g->xpos,g->ypos);
665   if (g->anchored)
666     fprintf(f," /anc:1");
667 
668 
669   fprintf(f," /w:[");
670   for (i = 0;i < N;i++)
671     if ((w = g->wires[i]))
672       fprintf(f," %d",w->nidx);
673     else
674       fprintf(f," -1");
675   fprintf(f," ]\n");
676 }
677 
678 /*
679   Add a stub to a joint with three wires.
680  */
joint_addstub(GCElement * g,EditState * es)681 void joint_addstub(GCElement *g,EditState *es)
682 {
683   int idx;
684   GWire *new_w1,*new_w2;
685   GWire *w = 0;			/* Any existing wire on joint */
686   int x1,y1,x2,y2;
687 
688   /*
689    * Find position
690    */
691   for (idx = 0;idx < 4;idx++)
692     if (!g->wires[idx]) break;
693 
694   if (g->wires[0])
695     w = g->wires[0];
696   else
697     w = g->wires[1];
698 
699   if (idx >= 4) return;
700 
701   wire_newNetSegment(es->env,w->net,&new_w1,&new_w2);
702 
703   GNet_draw(w->net);
704 
705   ob_touch(g);
706   ob_touch(new_w1);
707   ob_touch(new_w2);
708 
709   g->wires[idx] = new_w1;
710   new_w1->gate = g;
711   new_w1->orient = idx;
712   new_w2->orient = (idx+2)%4;
713 
714   x1 = g->xpos + (idx == 0)*2 + (idx == 2)*-2;
715   y1 = g->ypos + (idx == 3)*2 + (idx == 1)*-2;
716   x2 = g->xpos + (idx == 0)*12 + (idx == 2)*-12;
717   y2 = g->ypos + (idx == 3)*12 + (idx == 1)*-12;
718 
719   ob_touch(new_w1->nodes);
720   ob_touch(new_w2->nodes);
721 
722   new_w1->nodes->x = x1;
723   new_w1->nodes->y = y1;
724   new_w2->nodes->x = x2;
725   new_w2->nodes->y = y2;
726 
727   GWireNode_setends(new_w1->nodes);
728   wire_finalizeNet(new_w1);
729   GNet_draw(w->net);
730 }
731 
Joint_Rotate(GCElement * g,int centX,int centY,int rdir)732 void Joint_Rotate(GCElement *g, int centX, int centY,int rdir)
733 {
734   int x = g->xpos;
735   int y = g->ypos;
736 
737   ob_touch(g);
738   g->xpos = rotateX(x -centX,y - centY, rdir) + centX;
739   g->ypos = rotateY(x -centX,y - centY, rdir) + centY;
740 }
741 
Joint_wireSnap(GCElement * g,GWire * w,int * mod,int retry)742 GWireNode *Joint_wireSnap(GCElement *g,GWire *w,int *mod,int retry)
743 {
744   joint_fixwires(w->gate,w,retry);
745   return w->nodes;
746 }
747 
init_joint()748 void init_joint()
749 {
750   Pixmap P;
751 
752   P = Pixmap_registerFromFile("joint","joint.b");
753   gateinfo_1iconInit(&gate_joint_info,P,joint_iconDims,joint_iconBoldOffset);
754   RegisterGate(&gate_joint_info);
755 }
756 
757