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