1 /***************************************************************************
2 SCED - Schematic Capture Editor
3 JSPICE3 adaptation of Spice3e2 - Copyright (c) Stephen R. Whiteley 1992
4 Copyright 1990 Regents of the University of California.  All rights reserved.
5 Authors: 1981 Giles C. Billingsley  (parts of KIC layout editor)
6          1992 Stephen R. Whiteley
7 ****************************************************************************/
8 
9 /*
10  * The SCED selection code.
11  *
12  *
13  * The Selection code extensively uses the CD Info field.  The following
14  * convention is used:
15  *
16  *   Info = SQ_OLD     (0) Object is unselected.
17  *  *Info = SQ_OLDSEL  (1) Object is selected and in SelectionQ.
18  *   Info = SQ_GONE    (2) Object is conditionally deleted and in SelectionQ.
19  *   Info = SQ_NEW     (3) Object is conditionally created and in SelectionQ.
20  *  *Info = SQ_NEWSEL  (4) Object is conditionally created and in SelectionQ.
21  *   Info = SQ_INCMPLT (5) Object is being created.
22  *
23  *   Info = 6-11           Reserved.
24  *  *Info = 11-255         Object has conditionally new layer and is in
25  *                         SelectionQ.  OldLayer = Info - 10.
26  *
27  *  * means that SQShow will highlight these objects.
28  *
29  */
30 
31 #include "spice.h"
32 #include "sced.h"
33 #include <string.h>
34 
35 
36 
37 /***********************************************************************
38  *
39  * Current transform code.
40  *
41  ***********************************************************************/
42 
43 extern char *MenuMX;
44 extern char *MenuMY;
45 extern char *Menu0;
46 extern char *Menu90;
47 extern char *Menu180;
48 extern char *Menu270;
49 
50 
51 void
MX()52 MX()
53 
54 {
55     if (Parameters.kpMX) {
56         Parameters.kpMX = False;
57         MenuDeselect(MenuMX);
58     }
59     else {
60         Parameters.kpMX = True;
61         MenuSelect(MenuMX);
62     }
63 }
64 
65 
66 void
MY()67 MY()
68 
69 {
70     if (Parameters.kpMY) {
71         Parameters.kpMY = False;
72         MenuDeselect(MenuMY);
73     }
74     else {
75         Parameters.kpMY = True;
76         MenuSelect(MenuMY);
77     }
78 }
79 
80 
81 void
Rotat0()82 Rotat0()
83 
84 {
85     Parameters.kpRotationAngle = 90;
86     AlterMenuEntries(Menu0,Menu90);
87     MenuSelect(Menu90);
88 }
89 
90 
91 void
Rotat90()92 Rotat90()
93 
94 {
95     Parameters.kpRotationAngle = 180;
96     AlterMenuEntries(Menu90,Menu180);
97     MenuSelect(Menu180);
98 }
99 
100 
101 void
Rotat180()102 Rotat180()
103 
104 {
105     Parameters.kpRotationAngle = 270;
106     AlterMenuEntries(Menu180,Menu270);
107     MenuSelect(Menu270);
108 }
109 
110 
111 void
Rotat270()112 Rotat270()
113 
114 {
115     Parameters.kpRotationAngle = 0;
116     AlterMenuEntries(Menu270,Menu0);
117     MenuDeselect(Menu0);
118 }
119 
120 
121 
122 /***********************************************************************
123  *
124  * Selection operator code.
125  *
126  ***********************************************************************/
127 
128 extern char *MenuAREA;
129 extern char *MenuDESEL;
130 extern char *MenuSELEC;
131 extern char *MenuUNDO;
132 
133 #ifdef __STDC__
134 static struct ks *select_items(struct ka*,int);
135 static void sl_free(struct ks*);
136 static void sl_bb(struct ks*,struct ka*);
137 static void get_BB(struct o*,struct ka*);
138 static int  is_BB_visible(struct o*);
139 static void redisplay_edges(struct ka*);
140 static struct ks *which_cell(struct ks*);
141 static void sq_set_NEW(struct o*);
142 static void sq_delete_dups(void);
143 static void sq_display_selected(struct o*);
144 static int  overlap_path(struct p*,struct ka*);
145 static int  overlap_line(struct ka*,struct ka*);
146 static int  cross_line(struct ka*,struct ka*);
147 static int  point_in_poly(int,struct p*,long,long);
148 #else
149 static struct ks *select_items();
150 static void sl_free();
151 static void sl_bb();
152 static void get_BB();
153 static int  is_BB_visible();
154 static void redisplay_edges();
155 static struct ks *which_cell();
156 static void sq_set_NEW();
157 static void sq_delete_dups();
158 static void sq_display_selected();
159 static int  overlap_path();
160 static int  overlap_line();
161 static int  cross_line();
162 static int  point_in_poly();
163 #endif
164 
165 #define UpdateBB(BB2,BB1) \
166     if (BB1.kaLeft < BB2.kaLeft)     BB2.kaLeft = BB1.kaLeft; \
167     if (BB1.kaBottom < BB2.kaBottom) BB2.kaBottom = BB1.kaBottom; \
168     if (BB1.kaRight > BB2.kaRight)   BB2.kaRight = BB1.kaRight; \
169     if (BB1.kaTop > BB2.kaTop)       BB2.kaTop = BB1.kaTop;
170 
171 
172 
173 void
Sel(LookedAhead)174 Sel(LookedAhead)
175 
176 int *LookedAhead;
177 {
178     struct ka AOI;
179     int FirstTime = True;
180 
181     MenuSelect(MenuSELEC);
182     ShowPrompt("Point to select.");
183     loop {
184         switch (PointLoop(LookedAhead)) {
185             case PL_CMD:
186             case PL_ESC:
187                 goto quit;
188             case PL_UND:
189                 if (FirstTime == True) goto quit;
190                 MenuSelect(MenuUNDO);
191                 Selection(&AOI);
192                 MenuDeselect(MenuUNDO);
193                 continue;
194             case PL_PCW:
195                 AOI.kaLeft   = AOI.kaRight = SCursor.kcRawX;
196                 AOI.kaBottom = AOI.kaTop   = SCursor.kcRawY;
197                 ErasePrompt();
198                 Selection(&AOI);
199                 FirstTime = False;
200         }
201     }
202 quit:
203     MenuDeselect(MenuSELEC);
204     ErasePrompt();
205 }
206 
207 
208 void
Area(LookedAhead)209 Area(LookedAhead)
210 
211 int *LookedAhead;
212 {
213     struct ka AOI;
214     long OldRawX,OldRawY;
215     int FirstTime = True;
216 
217     MenuSelect(MenuAREA);
218 top:
219     loop {
220         ShowPrompt("Point to endpoints of diagonal.");
221         switch (PointLoop(LookedAhead)) {
222             case PL_ESC:
223             case PL_CMD:
224                 goto quit;
225             case PL_UND:
226                 if (FirstTime == True) goto quit;
227                 MenuSelect(MenuUNDO);
228                 Selection(&AOI);
229                 MenuDeselect(MenuUNDO);
230                 goto top;
231             case PL_PCW:
232                 FBSetRubberBanding('R');
233                 OldRawX = SCursor.kcRawX;
234                 OldRawY = SCursor.kcRawY;
235         }
236         ShowPrompt("Point to second endpoint.");
237         switch (PointLoop(LookedAhead)) {
238             case PL_ESC:
239             case PL_CMD:
240                 goto quit;
241             case PL_UND:
242                 FBSetRubberBanding(0);
243                 goto top;
244             case PL_PCW:
245                 FBSetRubberBanding(0);
246                 break;
247         }
248         AOI.kaLeft   = Min(OldRawX,SCursor.kcRawX);
249         AOI.kaBottom = Min(OldRawY,SCursor.kcRawY);
250         AOI.kaRight  = Max(OldRawX,SCursor.kcRawX);
251         AOI.kaTop    = Max(OldRawY,SCursor.kcRawY);
252         Selection(&AOI);
253         FirstTime = False;
254     }
255 
256 quit:
257     FBSetRubberBanding(0);
258     MenuDeselect(MenuAREA);
259     ErasePrompt();
260 }
261 
262 
263 void
Desel()264 Desel()
265 
266 {
267     MenuSelect(MenuDESEL);
268     SQComputeBB();
269     if (SelectQHead != NULL) {
270         SQClear();
271         /* Take care of Instance markers */
272         OversizeBox(&SelectQBB,200);
273         EraseBox(&SelectQBB);
274         Redisplay(&SelectQBB);
275     }
276     MenuDeselect(MenuDESEL);
277 }
278 
279 
280 void
Selection(AOI)281 Selection(AOI)
282 
283 /* Select items and link into SelectionQ, compute BB. */
284 struct ka *AOI;
285 {
286     struct ks *SList, *S;
287     struct ka BB,OldSelectQBB;
288 
289     if (AOI->kaLeft == AOI->kaRight) {
290         /* Point selection. */
291 
292         SList = select_items(AOI,True);
293         if (SList == NULL) return;
294         if (SList->ksPointer->oType == CDSYMBOLCALL) {
295             /* no geometry found, get one symbol */
296             S = which_cell(SList);
297             if (S) {
298                 if (S->ksPointer->oInfo == SQ_OLDSEL) {
299                     /* already selected, deselect and break */
300                     S->ksPointer->oInfo = SQ_OLD;
301                     SQDelete(S->ksPointer);
302                     get_BB(S->ksPointer,&BB);
303                     redisplay_edges(&BB);
304                 }
305                 else {
306                     /* add to select Q */
307                     S->ksPointer->oInfo = SQ_OLDSEL;
308                     SQInsert(S->ksPointer);
309                     sq_display_selected(S->ksPointer);
310                 }
311             }
312             SQComputeBB();
313             sl_free(SList);
314             return;
315         }
316         for (S = SList; S != NULL; S = S->ksSucc) {
317             /* keep only geometry */
318             if (S->ksPointer->oType == CDSYMBOLCALL) break;
319 
320             if (S->ksPointer->oInfo == SQ_OLDSEL) {
321                 /* already selected, deselect and break */
322                 S->ksPointer->oInfo = SQ_OLD;
323                 SQDelete(S->ksPointer);
324                 get_BB(S->ksPointer,&BB);
325                 EraseBox(&BB);
326                 Redisplay(&BB);
327                 DevUpdate();
328                 break;
329             }
330             else {
331                 /* add to select Q */
332                 S->ksPointer->oInfo = SQ_OLDSEL;
333                 SQInsert(S->ksPointer);
334                 sq_display_selected(S->ksPointer);
335             }
336         }
337         SQComputeBB();
338     }
339     else {
340 
341         /* Area select. */
342         SList = select_items(AOI,False);
343         if (SList == NULL) return;
344         SQComputeBB();
345         OldSelectQBB = SelectQBB;
346 
347         for (S = SList; S != NULL; S = S->ksSucc) {
348             if (S->ksPointer->oType == CDSYMBOLCALL &&
349                 !is_BB_visible(S->ksPointer)) continue;
350 
351             if (S->ksPointer->oInfo == SQ_OLDSEL) {
352                 /* already selected, deselect */
353                 S->ksPointer->oInfo = SQ_OLD;
354                 SQDelete(S->ksPointer);
355             }
356             else {
357                 /* add to select Q */
358                 S->ksPointer->oInfo = SQ_OLDSEL;
359                 SQInsert(S->ksPointer);
360             }
361         }
362         SQComputeBB();
363         UpdateBB(SelectQBB,OldSelectQBB);
364         if (SelectQBB.kaLeft == CDINFINITY) return;
365         EraseBox(&SelectQBB);
366         Redisplay(&SelectQBB);
367         DevUpdate();
368     }
369     sl_free(SList);
370 }
371 
372 
373 static struct ks *
select_items(AOI,PointSelect)374 select_items(AOI,PointSelect)
375 
376 /* Return a list of visible objects in the neighborhood of AOI as returned
377  * from the generator.  Only types in Parameters.kpSelectTypes are listed,
378  * all are listed if this is NULL.  The flag PointSelect is set for
379  * a point selection.
380  */
381 struct ka *AOI;
382 int PointSelect;
383 {
384     struct g *GenDesc;
385     struct o *Pointer;
386     struct ka BB;
387     struct ks *SPointer = NULL, *S;
388     struct p *Path;
389     long Width;
390     int Delta,Layer;
391 
392     if (PointSelect) {
393         /* expand the point to finite size */
394         if (SCursor.kcInFine == True)
395             Delta = 3.0/View->kvFineRatio;
396         else
397             Delta = 3.0/View->kvCoarseRatio;
398         OversizeBox(AOI,Delta);
399     }
400 
401     if (Not CDInitGen(Parameters.kpCellDesc,1,AOI->kaLeft,AOI->kaBottom,
402         AOI->kaRight,AOI->kaTop,&GenDesc)) MallocFailed();
403     loop {
404         CDGen(Parameters.kpCellDesc,GenDesc,&Pointer);
405         if (Pointer == NULL) break;
406         if (Pointer->oInfo == SQ_GONE) continue;
407         if (Parameters.kpSelectTypes &&
408             !strchr(Parameters.kpSelectTypes,Pointer->oType)) continue;
409 
410         switch (Pointer->oType) {
411             case CDWIRE:
412                 CDWire(Pointer,&Layer,&Width,&Path);
413                 if (PointSelect) {
414                     if (InPath(Delta+(int)Width/2,Path,
415                         AOI->kaLeft+Delta,AOI->kaBottom+Delta) != NULL)
416                         break;
417                 }
418                 else {
419                     if (overlap_path(Path,AOI)) break;
420                     if (InBox(Path->pX,Path->pY,AOI)) break;
421                 }
422                 continue;
423 
424             case CDPOLYGON:
425                 CDPolygon(Pointer,&Layer,&Path);
426                 if (PointSelect) {
427                     if (point_in_poly(Delta,Path,
428                         AOI->kaLeft+Delta,AOI->kaBottom+Delta))
429                         break;
430                 }
431                 else {
432                     if (point_in_poly(0,Path,AOI->kaLeft,AOI->kaBottom))
433                         break;
434                     if (overlap_path(Path,AOI)) break;
435                     if (InBox(Path->pX,Path->pY,AOI)) break;
436                 }
437                 continue;
438 
439             case CDLABEL:
440             case CDBOX:
441                 break;
442 
443             default:
444                 continue;
445         }
446         if (SPointer == NULL)
447             S = SPointer = alloc(ks);
448         else {
449             S->ksSucc = alloc(ks);
450             S = S->ksSucc;
451         }
452         if (S == NULL) MallocFailed();
453         S->ksPointer = Pointer;
454         S->ksSucc = NULL;
455     }
456 
457 
458     /* Now for the instances... */
459 
460     if (Parameters.kpSelectTypes &&
461         !strchr(Parameters.kpSelectTypes,CDSYMBOLCALL)) {
462         if (PointSelect) OversizeBox(AOI,-Delta);
463         return (SPointer);
464     }
465 
466     if (Not CDInitGen(Parameters.kpCellDesc,0,AOI->kaLeft,AOI->kaBottom,
467         AOI->kaRight,AOI->kaTop,&GenDesc)) MallocFailed();
468     loop {
469         CDGen(Parameters.kpCellDesc,GenDesc,&Pointer);
470         if (Pointer == NULL) break;
471 
472         if (Pointer->oInfo == SQ_GONE) continue;
473         if (SPointer == NULL)
474             S = SPointer = alloc(ks);
475         else {
476             S->ksSucc = alloc(ks);
477             S = S->ksSucc;
478         }
479         if (S == NULL) MallocFailed();
480         S->ksPointer = Pointer;
481         S->ksSucc = NULL;
482     }
483     if (PointSelect) OversizeBox(AOI,-Delta);
484     return (SPointer);
485 }
486 
487 
488 static void
sl_free(SList)489 sl_free(SList)
490 
491 /* Free a list as returned from select_items(). */
492 struct ks *SList;
493 {
494     struct ks *SQDesc,*SQNext;
495 
496     for (SQDesc = SList; SQDesc; SQDesc = SQNext) {
497         SQNext = SQDesc->ksSucc;
498         tfree(SQDesc);
499     }
500 }
501 
502 
503 static void
sl_bb(SList,BB)504 sl_bb(SList,BB)
505 
506 /* Compute the BB of the objects in SList. */
507 struct ks *SList;
508 struct ka *BB;
509 {
510     struct ks *S;
511     struct ka NBB,OBB;
512 
513     NBB.kaLeft   = CDINFINITY;
514     NBB.kaBottom = CDINFINITY;
515     NBB.kaRight  = -CDINFINITY;
516     NBB.kaTop    = -CDINFINITY;
517 
518     for (S = SList; S != NULL; S = S->ksSucc) {
519         get_BB(S->ksPointer,&OBB);
520         UpdateBB(NBB,OBB);
521     }
522     *BB = NBB;
523 }
524 
525 
526 static void
get_BB(Pointer,BB)527 get_BB(Pointer,BB)
528 
529 struct o *Pointer;
530 struct ka *BB;
531 {
532     if (Pointer->oType == CDLABEL)
533         BBLabel(View->kvCoarseWindow,Pointer,BB);
534     else
535         CDStatusInt = CDBB(Parameters.kpCellDesc,Pointer,&BB->kaLeft,
536             &BB->kaBottom,&BB->kaRight,&BB->kaTop);
537 }
538 
539 
540 static int
is_BB_visible(Pointer)541 is_BB_visible(Pointer)
542 
543 struct o *Pointer;
544 {
545     struct ka BB;
546 
547     /* will edges show in coarse window? */
548     CDStatusInt = CDBB(Parameters.kpCellDesc,Pointer,
549         &BB.kaLeft,&BB.kaBottom,&BB.kaRight,&BB.kaTop);
550     if (BB.kaLeft <= View->kvCoarseWindow->kaLeft &&
551         BB.kaRight  >= View->kvCoarseWindow->kaRight &&
552         BB.kaBottom <= View->kvCoarseWindow->kaBottom &&
553         BB.kaTop    >= View->kvCoarseWindow->kaTop)
554         return (False);
555     return (True);
556 }
557 
558 
559 static void
redisplay_edges(BB)560 redisplay_edges(BB)
561 
562 /* redisplay the edges of BB */
563 struct ka *BB;
564 {
565     struct ka EdgeOfBB;
566 
567     /* Left edge. */
568     EdgeOfBB.kaLeft   = BB->kaLeft-300;
569     EdgeOfBB.kaRight  = BB->kaLeft+300;
570     EdgeOfBB.kaTop    = BB->kaTop+300;
571     EdgeOfBB.kaBottom = BB->kaBottom-300;
572     EraseBox(&EdgeOfBB);
573     Redisplay(&EdgeOfBB);
574 
575     /* Right edge. */
576     EdgeOfBB.kaRight  = BB->kaRight+300;
577     EdgeOfBB.kaLeft   = BB->kaRight-300;
578     EdgeOfBB.kaTop    = BB->kaTop+300;
579     EdgeOfBB.kaBottom = BB->kaBottom-300;
580     EraseBox(&EdgeOfBB);
581     Redisplay(&EdgeOfBB);
582 
583     /* Bottom edge. */
584     EdgeOfBB.kaBottom = BB->kaBottom-300;
585     EdgeOfBB.kaTop    = BB->kaBottom+300;
586     EdgeOfBB.kaRight  = BB->kaRight;
587     EdgeOfBB.kaLeft   = BB->kaLeft;
588     EraseBox(&EdgeOfBB);
589     Redisplay(&EdgeOfBB);
590 
591     /* Top edge. */
592     EdgeOfBB.kaBottom = BB->kaTop-300;
593     EdgeOfBB.kaTop    = BB->kaTop+300;
594     EdgeOfBB.kaRight  = BB->kaRight;
595     EdgeOfBB.kaLeft   = BB->kaLeft;
596     EraseBox(&EdgeOfBB);
597     Redisplay(&EdgeOfBB);
598 
599     DevUpdate();
600 }
601 
602 
603 static struct ks *
which_cell(SList)604 which_cell(SList)
605 
606 /* Resolve ambiguity (multiple instances selected) */
607 struct ks *SList;
608 {
609     struct ks *S,*Sret;
610     char *SymbolName;
611     struct ka BB;
612     double A,Area;
613 
614     /* find the smallest cell */
615     sl_bb(SList,&BB);
616     Area = BB.kaRight - BB.kaLeft;
617     Area *= BB.kaTop - BB.kaBottom;
618     Sret = SList;
619     for (S = SList; S != NULL; S = S->ksSucc) {
620         CDStatusInt = CDBB(Parameters.kpCellDesc,S->ksPointer,
621             &BB.kaLeft,&BB.kaBottom,&BB.kaRight,&BB.kaTop);
622         A = BB.kaRight - BB.kaLeft;
623         A *= BB.kaTop - BB.kaBottom;
624         if (A < Area) {
625             Sret = S;
626             Area = A;
627         }
628     }
629     if (!is_BB_visible(Sret->ksPointer)) return (NULL);
630 
631     SymbolName = ((struct c *)Sret->ksPointer->oRep)->cMaster->mName;
632     sprintf(TypeOut,"You have selected an instance of %s.",SymbolName);
633     ShowPrompt(TypeOut);
634     return (Sret);
635 }
636 
637 
638 int
AreTypesInQ(Types)639 AreTypesInQ(Types)
640 
641 /* Returns True if one of Types is in SelectionQ and is selected,
642  * or of anything selected is in the Q if Types is NULL.
643  */
644 char *Types;
645 {
646     struct ks *SQDesc;
647 
648     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
649         if (Types == NULL || strchr(Types,SQDesc->ksPointer->oType))
650             if (SQDesc->ksPointer->oInfo == SQ_OLDSEL) return (True);
651     }
652     return (False);
653 }
654 
655 
656 void
SelectTypes(Types)657 SelectTypes(Types)
658 
659 /* Perform a point select, selecting only Types, or anything if
660  * Types is NULL.
661  */
662 char *Types;
663 {
664     struct ka BB;
665     char TTmp[8];
666 
667     BB.kaLeft   = BB.kaRight = SCursor.kcRawX;
668     BB.kaBottom = BB.kaTop   = SCursor.kcRawY;
669     if (Types == NULL) {
670         Selection(&BB);
671         return;
672     }
673     strcpy(TTmp,Parameters.kpSelectTypes);
674     strncpy(Parameters.kpSelectTypes,Types,8);
675     Parameters.kpSelectTypes[7] = '\0';
676     Selection(&BB);
677     strcpy(Parameters.kpSelectTypes,TTmp);
678 }
679 
680 
681 void
SQInit()682 SQInit()
683 
684 {
685     SelectQHead = NULL;
686 }
687 
688 
689 void
SQClear()690 SQClear()
691 
692 /* Clear the SelectionQ. */
693 {
694     struct ks *SQDesc,*SQNext;
695 
696     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQNext) {
697         SQNext = SQDesc->ksSucc;
698         SQDesc->ksPointer->oInfo = SQ_OLD;
699         tfree(SQDesc);
700     }
701     SelectQHead = NULL;
702 }
703 
704 
705 void
SQInsert(Pointer)706 SQInsert(Pointer)
707 
708 /* Insert Pointer into the SelectionQ (no checking for duplication). */
709 struct o *Pointer;
710 {
711     struct ks *SQDesc;
712 
713     if ((SQDesc = alloc(ks)) == NULL) MallocFailed();
714 
715     /* SelectQHead is most recent addition */
716     SQDesc->ksPointer = Pointer;
717     SQDesc->ksSucc = SelectQHead;
718     SelectQHead = SQDesc;
719 }
720 
721 
722 void
SQDelete(Pointer)723 SQDelete(Pointer)
724 
725 /* Delete Pointer from SelectionQ if it is there. */
726 struct o *Pointer;
727 {
728     struct ks *SQDesc,*SQPrev,*SQNext;
729 
730     SQPrev = NULL;
731 
732     for (SQDesc = SelectQHead; SQDesc; SQPrev = SQDesc,SQDesc = SQNext) {
733         SQNext = SQDesc->ksSucc;
734 
735         if (SQDesc->ksPointer != Pointer) continue;
736         Pointer->oInfo = SQ_OLD;
737 
738         if (SQPrev == NULL)
739             SelectQHead = SQNext;
740         else
741             SQPrev->ksSucc = SQNext;
742         tfree(SQDesc);
743         return;
744     }
745 }
746 
747 
748 void
SQComputeBB()749 SQComputeBB()
750 
751 /* Compute the BB of the queued and selected objects. */
752 {
753     struct ks *SQDesc;
754     struct ka BB;
755     int Info;
756 
757     SelectQBB.kaLeft   = CDINFINITY;
758     SelectQBB.kaBottom = CDINFINITY;
759     SelectQBB.kaRight  = -CDINFINITY;
760     SelectQBB.kaTop    = -CDINFINITY;
761 
762     for (SQDesc = SelectQHead; SQDesc != NULL; SQDesc = SQDesc->ksSucc) {
763         Info = SQDesc->ksPointer->oInfo;
764         if (Info == SQ_OLDSEL || Info == SQ_NEWSEL ||
765             (Info > 10 && Info <= 255)) {
766             get_BB(SQDesc->ksPointer,&BB);
767             UpdateBB(SelectQBB,BB);
768         }
769     }
770 }
771 
772 
773 /* Theory behind SQRestore() and SQDesel();
774  *
775  * In commands which modify objects such as Move, the original object(s)
776  * are conditionally deleted, and new objects are conditionally created.
777  * All objects are left in the SelectionQ for a time to allow Undo.
778  * For example, suppose the SelectionQ is empty, and the user points at
779  * an object.
780  *
781  *  operation:         SelectionQ:
782  *  select             OLD (Info = SQ_OLDSEL)
783  *  move               NEW (Info = SQ_NEW), OLD (Info = SQ_GONE)
784  * User now selects newly moved object (the complicated case):
785  *  select NEW         NEW (Info = SQ_OLDSEL), NEW (Info = SQ_OLDSEL),
786  *                     OLD (Info = SQ_GONE)
787  * Now undo undoes the selection
788  *  Undo (SQDesel())   NEW (Info = SQ_NEW), OLD (Info = SQ_GONE)
789  * Next undo undoes the move, leaving original item selected (conditionally).
790  *  Undo (SQRestore(1)) OLD (Info = SQ_NEWSEL)
791  * Next undo undoes the selection
792  *  Undo (SQDesel())   OLD (Info = SQ_NEW)
793  * The next undo repeats the move, etc.
794  *
795  * SQRestore(0) unsets the conditionality of objects in the SelectionQ
796  * and should be called when things are "final," i.e., before function
797  * exit or next operation.
798  */
799 
800 
801 void
SQRestore(Undo)802 SQRestore(Undo)
803 
804 /* Restore the conditionally created objects in the SelectionQ
805  * and delete duplicates if Undo is False.  Otherwise undo the
806  * last operation.
807  */
808 
809 /* If Undo is True:
810  *  Previously deleted objects become conditionally selected:
811  *   Info = SQ_GONE   -> Info = SQ_NEWSEL.
812  *  New objects are deleted from SelectionQ and database:
813  *   Info = SQ_NEW    deleted from queue and database.
814  *   Info = SQ_NEWSEL deleted from queue and database.
815  * Else
816  *  Conditionally deleted objects are really deleted:
817  *   Info = SQ_GONE   deleted from queue and database.
818  *  New objects are now Old objects, selected or otherwise:
819  *   Info = SQ_NEW    -> Info = SQ_OLD, deleted from queue.
820  *   Info = SQ_NEWSEL -> Info = SQ_OLDSEL.
821  */
822 
823 int Undo;
824 {
825     struct ks *SQDesc,*SQNext;
826     struct prpty *PDesc;
827     int Info;
828     struct o *Pointer;
829 
830     /* Properties added have the Info field set to "new", so we can
831      * remove them during Undo.
832      */
833     PDesc = Parameters.kpCellDesc->sPrptyList;
834     if (Undo == True) {
835         while (PDesc) {
836             if (PDesc->prpty_Info &&
837                 !strcmp(PDesc->prpty_Info,"new")) {
838                 Parameters.kpCellDesc->sPrptyList =
839                     PDesc->prpty_Succ;
840                 free(PDesc->prpty_Data);
841                 free(PDesc);
842                 PDesc = Parameters.kpCellDesc->sPrptyList;
843                 continue;
844             }
845             break;
846         }
847     }
848     else {
849         while (PDesc) {
850             if (PDesc->prpty_Info &&
851                 !strcmp(PDesc->prpty_Info,"new")) {
852                 PDesc->prpty_Info = NULL;
853                 PDesc = PDesc->prpty_Succ;
854                 continue;
855             }
856             break;
857         }
858     }
859 
860     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQNext) {
861         SQNext = SQDesc->ksSucc;
862         Pointer = SQDesc->ksPointer;
863         Info = Pointer->oInfo;
864 
865         if (Info == SQ_GONE) {
866             if (Undo == True)
867                 Pointer->oInfo = SQ_NEWSEL;
868             else {
869                 SQDelete(Pointer);
870                 CDDelete(Parameters.kpCellDesc,Pointer);
871             }
872             continue;
873         }
874         if (Info == SQ_NEW) {
875             SQDelete(Pointer);
876             if (Undo == True) {
877                 Pointer->oInfo = SQ_GONE;
878                 CDDelete(Parameters.kpCellDesc,Pointer);
879             }
880             else
881                 Pointer->oInfo = SQ_OLD;
882             continue;
883         }
884         if (Info == SQ_NEWSEL) {
885             if (Undo == True) {
886                 SQDelete(Pointer);
887                 Pointer->oInfo = SQ_GONE;
888                 CDDelete(Parameters.kpCellDesc,Pointer);
889             }
890             else
891                 Pointer->oInfo = SQ_OLDSEL;
892         }
893     }
894     if (Undo == False)
895         sq_delete_dups();
896 }
897 
898 
899 void
SQDesel(Types)900 SQDesel(Types)
901 
902 /* Undo a selection operation. */
903 
904 /* Info = SQ_OLDSEL deleted from queue, and duplicates set to Info = SQ_NEW.
905  * Info = SQ_NEWSEL -> Info = SQ_NEW.
906  * Ignores objects with type not listed in Types.
907  */
908 
909 char *Types;
910 {
911     struct ks *SQDesc, *SQNext;
912     struct o *Pointer;
913     int Info;
914     char Type;
915 
916     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQNext) {
917         SQNext = SQDesc->ksSucc;
918         Type = SQDesc->ksPointer->oType;
919         if (Types And !strchr(Types,Type)) continue;
920         Info = SQDesc->ksPointer->oInfo;
921 
922         /* Have to be careful here.  If selected object is already
923          * in queue have to reset Info of second entry to SQ_NEW.
924          */
925 
926         if (Info == SQ_OLDSEL) {
927             Pointer = SQDesc->ksPointer;
928             Pointer->oInfo = SQ_OLD;
929             SQDelete(Pointer);
930             sq_set_NEW(Pointer);
931             continue;
932         }
933         if (Info == SQ_NEWSEL)
934             SQDesc->ksPointer->oInfo = SQ_NEW;
935     }
936 }
937 
938 
939 void
SQShow()940 SQShow()
941 
942 /* Show selected objects by highlighting their BBs. */
943 {
944     struct ks *SQDesc;
945     int Info;
946 
947     if (Not Parameters.kpEnableSelectQRedisplay) return;
948     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
949         Info = SQDesc->ksPointer->oInfo;
950         /* Test for user interrupt */
951         if (Parameters.kpSIGINTERRUPT) {
952             RedisplayAfterInterrupt();
953             return;
954         }
955         if (Info == SQ_OLDSEL || Info == SQ_NEWSEL ||
956             (Info > 10 && Info <= 255)) {
957             /* Show Selected Objects */
958             sq_display_selected(SQDesc->ksPointer);
959         }
960     }
961 }
962 
963 
964 static void
sq_set_NEW(Pointer)965 sq_set_NEW(Pointer)
966 
967 /* If object is in queue, set Info to SQ_NEW. */
968 struct o *Pointer;
969 {
970     struct ks *SQDesc;
971     int Info;
972 
973     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
974         if (SQDesc->ksPointer == Pointer) {
975             SQDesc->ksPointer->oInfo = SQ_NEW;
976             return;
977         }
978     }
979 }
980 
981 
982 static void
sq_delete_dups()983 sq_delete_dups()
984 
985 /* Delete duplicate entries in selection queue. */
986 {
987     struct ks *SQDesc,*SQDesc1,*SQNext;
988     int Info;
989 
990     for (SQDesc = SelectQHead; SQDesc; SQDesc = SQNext) {
991         SQNext = SQDesc->ksSucc;
992         Info = SQDesc->ksPointer->oInfo;
993         for (SQDesc1 = SQDesc->ksSucc; SQDesc1; SQDesc1 = SQDesc1->ksSucc)
994             if (SQDesc->ksPointer == SQDesc1->ksPointer) {
995                 SQDelete(SQDesc->ksPointer);
996                 /* SQDelete sets Info to SQ_OLD, have to undo this */
997                 SQDesc1->ksPointer->oInfo = Info;
998                 break;
999             }
1000     }
1001 }
1002 
1003 
1004 static void
sq_display_selected(Pointer)1005 sq_display_selected(Pointer)
1006 
1007 struct o *Pointer;
1008 {
1009     int Layer;
1010     struct p *Path;
1011     long Width;
1012     char OldRD;
1013     struct ka BB;
1014 
1015     if (Pointer->oType == CDWIRE) {
1016         CDWire(Pointer,&Layer,&Width,&Path);
1017         ShowWire(HighlightingColor,Width,Path);
1018         return;
1019     }
1020     if (Pointer->oType == CDSYMBOLCALL) {
1021         CDStatusInt = CDBB(Parameters.kpCellDesc,Pointer,
1022             &BB.kaLeft,&BB.kaBottom,&BB.kaRight,&BB.kaTop);
1023         ShowEmptyBox(HighlightingColor,&BB);
1024         return;
1025     }
1026     if (Pointer->oType == CDLABEL) {
1027 
1028         /* BB of labels must be special-cased.  Can't use CDBB. */
1029 
1030         OldRD = Parameters.kpRedisplayControl;
1031         if (OldRD != FINEVIEWPORTONLY) {
1032             Parameters.kpRedisplayControl = COARSEVIEWPORTONLY;
1033             BBLabel(View->kvCoarseWindow,Pointer,&BB);
1034             ShowEmptyBox(HighlightingColor,&BB);
1035         }
1036         if (OldRD != COARSEVIEWPORTONLY) {
1037             Parameters.kpRedisplayControl = FINEVIEWPORTONLY;
1038             BBLabel(View->kvFineWindow,Pointer,&BB);
1039             ShowEmptyBox(HighlightingColor,&BB);
1040         }
1041         Parameters.kpRedisplayControl = OldRD;
1042         return;
1043     }
1044     if (Pointer->oType == CDPOLYGON) {
1045         CDPolygon(Pointer,&Layer,&Path);
1046         ShowPath(HighlightingColor,Path,True);
1047         return;
1048     }
1049     if (Pointer->oType == CDBOX) {
1050         CDStatusInt = CDBB(Parameters.kpCellDesc,Pointer,
1051             &BB.kaLeft,&BB.kaBottom,&BB.kaRight,&BB.kaTop);
1052         ShowEmptyBox(HighlightingColor,&BB);
1053         return;
1054     }
1055 }
1056 
1057 
1058 long *
InPath(Delta,Path,X,Y)1059 InPath(Delta,Path,X,Y)
1060 
1061 /* Is (X,Y) on the path described by <Path>?
1062  * If yes, return a pointer to x,y that are exactly in path.
1063  */
1064 int Delta;
1065 struct p *Path;
1066 long X,Y;
1067 {
1068     struct p *Pair;
1069     struct ka BB;
1070     double x1,x2,y1,y2,d0,d1,d2,d3,w;
1071     static long xy[2];
1072 
1073     if (Delta < 10) Delta = 10;
1074 
1075     if (Path == NULL) return (False);
1076     for (Pair = Path; Pair->pSucc != NULL; Pair = Pair->pSucc) {
1077 
1078         if (Pair->pX < Pair->pSucc->pX) {
1079             BB.kaLeft = Pair->pX;
1080             BB.kaRight = Pair->pSucc->pX;
1081         }
1082         else {
1083             BB.kaRight = Pair->pX;
1084             BB.kaLeft = Pair->pSucc->pX;
1085         }
1086         if (Pair->pY < Pair->pSucc->pY) {
1087             BB.kaBottom = Pair->pY;
1088             BB.kaTop = Pair->pSucc->pY;
1089         }
1090         else {
1091             BB.kaBottom = Pair->pSucc->pY;
1092             BB.kaTop = Pair->pY;
1093         }
1094         OversizeBox(&BB,Delta);
1095         if (!InBox(X,Y,&BB)) continue;
1096 
1097         x1 = Pair->pX - Pair->pSucc->pX;
1098         y1 = Pair->pY - Pair->pSucc->pY;
1099         d0 = x1*x1 + y1*y1;
1100 
1101         x1 = Pair->pX - X;
1102         y1 = Pair->pY - Y;
1103         d1 = x1*x1 + y1*y1;
1104         x2 = Pair->pSucc->pX - X;
1105         y2 = Pair->pSucc->pY - Y;
1106         d2 = x2*x2 + y2*y2;
1107 
1108         d3 = (d2 - d1)/(2*sqrt(d0));
1109         w = (d1+d2)/2 - d0/4 - d3*d3;
1110 
1111         if (w <= (long)Delta*Delta) {
1112             /* should be positive, fabs() just in case */
1113             d1 = sqrt(fabs(d1-w)/d0);
1114             xy[0] = Pair->pX + (Pair->pSucc->pX - Pair->pX)*d1;
1115             xy[1] = Pair->pY + (Pair->pSucc->pY - Pair->pY)*d1;
1116             return (xy);
1117         }
1118     }
1119     return (NULL);
1120 }
1121 
1122 
1123 static int
point_in_poly(Delta,Path,X,Y)1124 point_in_poly(Delta,Path,X,Y)
1125 
1126 /* Return True if point is enclosed in polygon, or within Delta of a vertex.
1127  * Algorithm is to sum angle differences to reference point around
1128  * path.  If the reference point is inside, the sum is 2*PI, otherwise
1129  * the sum is zero.
1130  */
1131 int Delta;
1132 struct p *Path;
1133 long X,Y;
1134 {
1135     struct p *p;
1136     double Xp,Yp,R,Theta,ThetaLast,Sum,zz;
1137 
1138     Sum =  0;
1139 
1140     for (p = Path; p != NULL; p = p->pSucc) {
1141 
1142         Xp = p->pX - X;
1143         Yp = p->pY - Y;
1144         R = sqrt(Xp*Xp + Yp*Yp);
1145         if (R <= Delta) return (True);
1146         Theta = asin(Yp/R);
1147         if (Xp >= 0) {
1148             if (Yp < 0)
1149                 Theta = 2*M_PI + Theta;
1150         }
1151         else
1152             Theta = M_PI - Theta;
1153         if (p != Path) {
1154             zz = (Theta - ThetaLast);
1155             if (zz > M_PI) zz -= 2*M_PI;
1156             if (zz < -M_PI) zz += 2*M_PI;
1157             Sum += zz;
1158         }
1159         ThetaLast = Theta;
1160     }
1161     if (fabs(Sum) >= 1.99*M_PI) return (True);
1162     return (False);
1163 }
1164 
1165 
1166 static int
overlap_path(Path,BB)1167 overlap_path(Path,BB)
1168 
1169 /* return True if the path intersects the BB */
1170 struct p *Path;
1171 struct ka *BB;
1172 {
1173     struct ka Line;
1174 
1175     Line.kaLeft = Path->pX;
1176     Line.kaBottom = Path->pY;
1177     Path = Path->pSucc;
1178     for (; Path != NULL; Path = Path->pSucc) {
1179         Line.kaRight = Path->pX;
1180         Line.kaTop = Path->pY;
1181         if (overlap_line(&Line,BB)) return (True);
1182         Line.kaLeft = Line.kaRight;
1183         Line.kaBottom = Line.kaTop;
1184     }
1185     return (False);
1186 }
1187 
1188 
1189 static int
overlap_line(Line,BB)1190 overlap_line(Line,BB)
1191 
1192 /* return True if Line intersects BB */
1193 struct ka *Line,*BB;
1194 {
1195     struct ka LBB;
1196 
1197     LBB.kaLeft = BB->kaLeft;
1198     LBB.kaRight = BB->kaLeft;
1199     LBB.kaBottom = BB->kaBottom;
1200     LBB.kaTop = BB->kaTop;
1201     if (cross_line(Line,&LBB)) return (True);
1202 
1203     LBB.kaRight = BB->kaRight;
1204     LBB.kaBottom = BB->kaTop;
1205     if (cross_line(Line,&LBB)) return (True);
1206 
1207     LBB.kaLeft = BB->kaRight;
1208     LBB.kaBottom = BB->kaBottom;
1209     if (cross_line(Line,&LBB)) return (True);
1210 
1211     LBB.kaLeft = BB->kaLeft;
1212     LBB.kaTop = BB->kaBottom;
1213     if (cross_line(Line,&LBB)) return (True);
1214 
1215     return (False);
1216 }
1217 
1218 
1219 static int
cross_line(Line,BB)1220 cross_line(Line,BB)
1221 
1222 /* return True if line segments stored as diagonal of BB, Line
1223  * intersect.  The line in BB is Manhattan.
1224  */
1225 struct ka *Line,*BB;
1226 {
1227     struct ka LineBB,MBB;
1228     long X,Y;
1229 
1230     MBB = *BB;
1231     if (MBB.kaTop < MBB.kaBottom)
1232         SwapInts(MBB.kaTop,MBB.kaBottom);
1233     if (MBB.kaRight < MBB.kaLeft)
1234         SwapInts(MBB.kaRight,MBB.kaLeft);
1235     LineBB = *Line;
1236     if (LineBB.kaTop < LineBB.kaBottom)
1237         SwapInts(LineBB.kaTop,LineBB.kaBottom);
1238     if (LineBB.kaRight < LineBB.kaLeft)
1239         SwapInts(LineBB.kaRight,LineBB.kaLeft);
1240 
1241     /* return False if BB's don't overlap */
1242     if (LineBB.kaLeft > MBB.kaRight ||
1243         LineBB.kaRight < MBB.kaLeft ||
1244         LineBB.kaBottom > MBB.kaTop ||
1245         LineBB.kaTop < MBB.kaBottom)
1246         return (False);
1247 
1248     /* if Line is Manhattan, return True */
1249     if (Line->kaLeft == Line->kaRight || Line->kaBottom == Line->kaTop)
1250         return (True);
1251 
1252     if (BB->kaBottom == BB->kaTop) {
1253 
1254         X = (BB->kaBottom - Line->kaBottom)*
1255             ((double)(Line->kaRight - Line->kaLeft)/
1256             (Line->kaTop - Line->kaBottom)) +
1257             Line->kaLeft;
1258         if (X < BB->kaLeft || X > BB->kaRight) return (False);
1259     }
1260     else {
1261 
1262         Y = (BB->kaLeft - Line->kaLeft)*
1263             ((double)(Line->kaTop - Line->kaBottom)/
1264             (Line->kaRight - Line->kaLeft))
1265             + Line->kaBottom;
1266         if (Y < BB->kaBottom || Y > BB->kaTop) return (False);
1267     }
1268     return (True);
1269 }
1270