1 /*
2  * @(#)short.c	1.2	01/03/85
3  *
4  * Routines for the "short" commands of the SUN Gremlin picture editor.
5  *
6  * Mark Opperman (opcode@monet.BERKELEY)
7  *
8  */
9 
10 #include <suntool/tool_hs.h>
11 #include "icondata.h"
12 #include "gremlin.h"
13 
14 /* imports from graphics files */
15 
16 extern GRClear();
17 extern GRCurrentSet();
18 extern GRCurrentSetOn();
19 extern GRCurrentSetOff();
20 extern GRDisplayPoint();
21 extern GRSetCurve();
22 extern GRSetTextPos();
23 
24 /* imports from undodb.c */
25 
26 extern UNForget();
27 
28 /* imports from display.c */
29 
30 extern DISClearSetDisplay();
31 extern DISScreenAdd();
32 extern DISScreenErase();
33 
34 /* imports from database files */
35 
36 extern ELT *DBCopy();
37 extern ELT *DBCreateElt();
38 
39 extern POINT *PTMakePoint();
40 extern POINT *PTMakeTextPoints();
41 
42 extern DBDelete();
43 extern DBClearElt();
44 extern DBXform();
45 extern DBBounded();
46 extern DBAddSet();
47 extern DBClearSet();
48 
49 /* imports from long.c */
50 
51 extern LGIncludeSet();
52 extern CP();			/* clear points */
53 extern CSP();			/* clear show points */
54 
55 /* imports from menu.c  */
56 
57 extern MNHighLt();
58 extern MNUnHighLt();
59 
60 extern HiBrush[];
61 extern HiBuffer[];
62 extern HiFont[];
63 extern HiMode[];
64 extern HiSize[];
65 
66 int adj[4] = { 0, 0, 1, 2 };
67 
68 /* imports from help.c */
69 
70 extern help();
71 
72 /* imports from text.c */
73 
74 extern TxMsgOK();
75 extern TxPutMsg();
76 extern text_putvalue();
77 extern text_restorebuf();
78 
79 /* imports from C */
80 
81 extern char *malloc();
82 
83 /* imports from main.c */
84 
85 extern ELT *PICTURE;                /* current PICTURE database      */
86 extern ELT *cset;                   /* current set database          */
87 extern ELT *MEN[];                  /* pointers for user symbols     */
88 extern ELT arhead;                  /* arrow head template           */
89 
90 extern POINT MENPOINT[];            /* pointers used fo user symbols */
91 extern POINT *POINTLIST;            /* accumulated point list        */
92 
93 extern CBRUSH;        		    /* current brush                 */
94 extern Gridon;                      /* grid mode flag                */
95 extern SEQ;                         /* point sequence number         */
96 extern Adjustment;                  /* point adjustment mode         */
97 extern GravityOn;                   /* gravity mode flag             */
98 extern CHANGED;                     /* PICTURE changed flag          */
99 extern CsetOn;			    /* current set displayed on	     */
100 
101 extern (*lastcommand)();	    /* previous command */
102 extern lasttext;		    /* TRUE if previous command wants text */
103 extern struct pixwin *pix_pw;
104 
105 extern SHUpdate(), SHAgain(), SHDrawArc(), SHDrawCurve(), SHCopy(),
106        SHDefineSet(), SHErase(), SHSetArea(), SHGravity(),
107        SHGrid(), SHRotate(), SHScale(), SHTranslate(),
108        SHDrawVector(), SHMAdjust(), SHBox(), SHArrow(),
109        SHSave1(), SHSave2(), SHSave3(), SHSave4();
110 
111 extern LGUndo();
112 
113 #define twoPi 6.2832
114 
115 static char nopnt[18] = "not enough points";
116 static char noset[15] = "no current set";
117 
118 /*
119  * The following two arrays define the short commands and the routines
120  * that process them.
121  */
122 static char shcmds[] = { '\14', '.', '1', '2', '3', '4', '?', 'a', 'b',
123                           'c', 'd', 'e', 'f', 'g', 'q', 'r',
124                           's', 't', 'u', 'v', 'w', 'x', 'z', '\0'};
125 
126 static (*(shrtns[]))() = {
127     SHUpdate,			/* redraw screen */
128     SHAgain,			/* repeat last command */
129     SHSave1,                    /* save user symbol */
130     SHSave2,                    /* save user symbol */
131     SHSave3,                    /* save user symbol */
132     SHSave4,                    /* save user symbol */
133     help,			/* help screen */
134     SHDrawArc,			/* draw arc */
135     SHDrawCurve,		/* draw curve */
136     SHCopy,			/* copy current set */
137     SHDefineSet,		/* define current set */
138     SHErase,			/* erase elements */
139     SHSetArea,			/* select area for current set */
140     SHGravity,			/* gravity */
141     SHGrid,			/* toggle grid display */
142     SHRotate,			/* rotate current set */
143     SHScale,			/* scale current set */
144     SHTranslate,		/* translate current set */
145     LGUndo,			/* undo last command */
146     SHDrawVector,		/* draw vectors */
147     SHArrow,                    /* arrowhead */
148     SHBox,                      /* rectangle from two points */
149     SHMAdjust			/* manhattan adjust */
150 };
151 
152 
153 /*
154  * SHLookup searches a table of characters to find one that matches a
155  * the given character.
156  * If c is a valid command character, its index is returned;
157  * if c is the null command, -2 is returned, else -1 is returned.
158  */
159 SHLookup(c, table)
160 char c;				/* char to be looked up */
161 register char table[];		/* pointer to the valid commands */
162 {
163     register index;
164 
165     if ((c == ' ') || (c == '\0'))
166 	return(-2);
167 
168     for (index=0; table[index] != '\0'; index++) {
169         if (table[index] == c)
170 	    return(index);
171         if (table[index] > c)
172 	    return(-1);
173     }
174 
175     return(-1);
176 }  /* end SHLookup */
177 
178 
179 /*
180  * This routine reads in, looks up, and executes a short command.
181  */
182 SHCommand(command)
183 register char *command;
184 {
185     register index;
186 
187     if ((index = SHLookup(*command, shcmds)) == -2)
188 	return;
189 
190     if (index >= 0) {
191 	GRCurrentSetOn();
192 	TxMsgOK();
193 	(*(shrtns[index]))();
194     }
195     else
196 	error("no such command");
197 }  /* end SHCommand */
198 
199 
200 /*
201  * Repeat previous command.
202  */
203 SHAgain()
204 {
205     if (lasttext)
206 	text_restorebuf();
207     (*lastcommand)();
208 }
209 
210 
211 /*
212  * This routine creates and displays a VECTOR element from the
213  * points previously specified.
214  */
215 SHDrawVector()
216 {
217     register POINT *p1, *p2;
218     register ELT *e1;
219     POINT *plist;
220     char *txt;
221 
222     if (SEQ < 2) {      /* not enough points */
223 	error(nopnt);
224 	return;
225     }
226 
227     UNForget();
228 
229     DISClearSetDisplay();
230     DBClearSet();
231 
232     plist = PTInit();
233     p1 = POINTLIST;
234     (void) PTMakePoint(p1->x, p1->y, &plist);
235     p2 = PTNextPoint(p1);
236 
237     while (!Nullpoint(p2)) {
238 	(void) PTMakePoint(p2->x, p2->y, &plist);
239 	p1 = p2;
240 	p2 = PTNextPoint(p1);
241     }
242 
243     txt = malloc(1);
244     *txt = '\0';
245     e1 = DBCreateElt(VECTOR, plist, CBRUSH, 0, txt, &PICTURE);
246     DISScreenAdd(e1, pixmask | csetmask);
247     DBAddSet(e1);
248 
249     CP();
250     CHANGED = TRUE;
251 }  /* end SHDrawVector */
252 
253 
254 /*
255  * This routine creates and displays an ARC element based on the
256  * points previously defined.  If 3 or more points are defined, the
257  * extent of the arc is calculated as the angle formed by the
258  * respective lines through the second and third points and the first
259  * point.  If only 2 points are specified, a full circle is drawn.
260  */
261 SHDrawArc()
262 {
263     register POINT *p1, *p2;
264     register ELT *e1;
265     POINT *plist;
266     char *txt;
267     float a1, a2, angle, radius;
268 
269     if (SEQ < 2) {       /* not enough points */
270 	error(nopnt);
271 	return;
272     }
273 
274     UNForget();
275 
276     plist = PTInit();
277     p1 = POINTLIST;
278     p2 = PTNextPoint(p1);
279     radius = sqrt((double) ((p2->x - p1->x) * (p2->x - p1->x) +
280 			    (p2->y - p1->y) * (p2->y - p1->y)));
281     if (radius == 0.0) {
282 	error("zero radius");
283 	return;
284     }
285 
286     CSP();
287 
288     if (SEQ == 2) {       /* draw full circle */
289 	angle = 0;
290         /* Add extra positioning points  */
291 	(void) PTMakePoint(p1->x, p1->y, &plist);
292 	(void) PTMakePoint(p2->x, p2->y, &plist);
293 	(void) PTMakePoint(p1->x, p1->y + radius, &plist);
294 	(void) PTMakePoint(p1->x, p1->y - radius, &plist);
295 	(void) PTMakePoint(p1->x + radius, p1->y, &plist);
296 	(void) PTMakePoint(p1->x - radius, p1->y, &plist);
297     }
298     else {
299 	(void) PTMakePoint(POINTLIST->x, POINTLIST->y, &plist);
300 	p1 = PTNextPoint(POINTLIST);
301 	(void) PTMakePoint(p1->x, p1->y, &plist);
302 	p2 = PTNextPoint(p1);
303 	a1 =  atan2((p1->x - POINTLIST->x), (p1->y - POINTLIST->y));
304 	a2 =  atan2((p2->x - POINTLIST->x), (p2->y - POINTLIST->y));
305 	angle = a1 - a2;
306 	if (angle < 0.0)
307 	    angle += twoPi;
308 
309 	/* Set second point to lie on arc */
310 	(void) PTMakePoint((radius * sin(a2) + POINTLIST->x),
311 			   (radius * cos(a2) + POINTLIST->y), &plist);
312 	angle *= 360.0/twoPi;      /* convert to degrees */
313     }
314 
315     DISClearSetDisplay();
316     DBClearSet();
317 
318     txt = malloc(1);
319     *txt = '\0';
320     e1 = DBCreateElt(ARC, plist, CBRUSH, (int) (angle + 0.5), txt, &PICTURE);
321 
322     DISScreenAdd(e1, pixmask | csetmask);
323     DBAddSet(e1);
324 
325     CP();
326     CHANGED = TRUE;
327 }  /* end SHDrawARc */
328 
329 
330 /*
331  * Draw curve object.
332  */
333 SHDrawCurve()
334 {
335     register POINT *p1;
336     register ELT *e1;
337     POINT *plist;
338     char *txt;
339 
340     if (SEQ < 2) {
341 	error("need at least 2 points");
342 	return;
343     }
344 
345     plist = PTInit();
346     p1 = POINTLIST;
347 
348     do {
349 	(void) PTMakePoint(p1->x, p1->y, &plist);
350 	p1 = PTNextPoint(p1);
351     } while (!Nullpoint(p1));
352 
353     if (GRSetCurve(plist) != 0) {
354 	error("too many consecutive knots at same place");
355 	return;
356     }
357 
358     UNForget();
359 
360     txt = malloc(1);
361     *txt = '\0';
362     e1 = DBCreateElt(CURVE, plist, CBRUSH, 0, txt, &PICTURE);
363 
364     DISClearSetDisplay();
365     DBClearSet();
366     DISScreenAdd(e1, pixmask | csetmask);
367     DBAddSet(e1);
368 
369     CP();
370     CHANGED = TRUE;
371 }  /* end SHDrawCurve */
372 
373 
374 /*
375  * This routine erases selected elements from the screen and deletes
376  * them from the picture database.
377  */
378 SHErase()
379 {
380     register ELT *e1;
381 
382     if (DBNullelt(cset)) {
383 	error(noset);
384 	return;
385     }
386 
387     UNForget();
388 
389     fasterase();
390 
391     while (!DBNullelt(cset)) {   /* delete elements in current set */
392 /*
393 	DISScreenErase(cset, pixmask | csetmask);
394 */
395 	e1 = DBNextofSet(cset);
396 	DBDelete(cset, &PICTURE);
397 	cset = e1;
398     }
399 
400     CHANGED = TRUE;
401 }  /* end SHErase */
402 
403 
404 /*
405  * This routine toggles the gravity mode.
406  */
407 SHGravity()
408 {
409     if (GravityOn = !GravityOn)
410 	MNHighLt(HiMode[3]);
411     else
412 	MNUnHighLt(HiMode[3]);
413 }  /* End GravityOn */
414 
415 
416 /*
417  * This routine toggles the display of the grid.
418  */
419 SHGrid()
420 {
421     if (Gridon = !Gridon)
422 	GRDisplayGrid();
423     else
424 	GRBlankGrid();
425 }  /* end SHGrid */
426 
427 
428 /*
429  * Manhattan Adjust -
430  * This routine toggles the adjustment mode.
431  */
432 SHMAdjust()
433 {
434     if (Adjustment == MAN) {
435 	MNUnHighLt(HiMode[adj[MAN]]);
436 	Adjustment = NOADJ;
437     }
438     else {
439 	if (Adjustment != NOADJ)
440 	    MNUnHighLt(HiMode[adj[Adjustment]]);
441 	MNHighLt(HiMode[adj[MAN]]);
442 	Adjustment = MAN;
443     }
444 }  /* end SHMAdjust */
445 
446 
447 /*
448  * This routine defines the current set based upon previously
449  * defined points to select elements.  If no points are specified
450  * the entire picture becomes the current set.  In this case, the
451  * old current set is not erased (optimization) and any elements
452  * not in it are added.  Otherwise, the new current set can not be
453  * guaranteed to contain the old current set, and so it is erased
454  * and set to empty before adding the new elements.
455  */
456 SHDefineSet()
457 {
458     if (SEQ > 0) {		/* redefine current set */
459 	DISClearSetDisplay();
460 	DBClearSet();
461     }
462 
463     CSP();
464     LGIncludeSet();
465 }  /* end SHDefineSet */
466 
467 
468 /*
469  * This routine defines the current set by selecting all elements
470  * bounded by a rectangle whose diagonal is defined by specifed points.
471  */
472 SHSetArea()
473 {
474     if (SEQ < 2) {
475 	error(nopnt);
476 	return;
477     }
478 
479     if (DBNullelt(PICTURE))
480 	return;
481 
482     DISClearSetDisplay();
483     DBClearSet();
484 
485     SHMSetArea();
486 }  /* end SHSetArea */
487 
488 
489 /*
490  * This routine ADDS to the current set all elements bounded by a
491  * rectangle whose diagonal is defined by two specifed points.
492  */
493 SHMSetArea()
494 {
495     register ELT *e1;
496     float x1, y1, x2, y2;
497 
498     if (SEQ < 2) {
499 	error(nopnt);
500 	return;
501     }
502 
503     if (DBNullelt(PICTURE))
504 	return;
505 
506     x1 = POINTLIST->x;
507     y1 = POINTLIST->y;
508     x2 = PTNextPoint(POINTLIST)->x;
509     y2 = PTNextPoint(POINTLIST)->y;
510     e1 = PICTURE;
511 
512     while (!DBNullelt(e1)) {
513 	if (DBBounded(e1, x1, y1, x2, y2)) {
514 	    DISScreenAdd(e1, csetmask);
515 	    DBAddSet(e1);
516 	}
517 	e1 = DBNextElt(e1);
518     }
519 
520     CP();
521 }  /* end SHMSetArea */
522 
523 
524 /*
525  * This routine translates the elements in the current set as defined
526  * by points.  The translation is accomplished by defining a transformation
527  * matrix and calling DBXform.
528  */
529 SHTranslate()
530 {
531     register ELT *e1;
532     register POINT *p1;
533     float xmat[3][2];
534 
535     if (DBNullelt(cset)) {
536 	error(noset);
537 	return;
538     }
539 
540     if (SEQ < 2) {      /* not enough points */
541 	error(nopnt);
542 	return;
543     }
544 
545     UNForget();
546 
547     p1 = PTNextPoint(POINTLIST);
548     xmat[0][0] = xmat[1][1] = 1;    /* set up translation matrix */
549     xmat[1][0] = xmat[0][1] = 0;
550     xmat[2][0] = p1->x - POINTLIST->x;
551     xmat[2][1] = p1->y - POINTLIST->y;
552     e1 = cset;
553 
554     fasterase();
555 
556     while (!DBNullelt(e1)) {
557 	/*
558 	DISScreenErase(e1, pixmask | csetmask);
559 	*/
560 	TxMsgOK();
561 	DBXform(e1, xmat, &PICTURE);
562 	DISScreenAdd(e1, pixmask | csetmask);
563 	e1 = DBNextofSet(e1);
564     }
565 
566     CP();
567     CHANGED = TRUE;
568 }  /* end SHTranslate */
569 
570 
571 /*
572  * This routine copies the elements in the current set as defined
573  * by points.  To copy, the current set pointer is cleared so that new
574  * elements as added by DBCopy can be used to comprise the new current
575  * set.  A pointer is maintained to the old current set which is traversed
576  * to determine the elements to be copied.  This process continues for all
577  * points specified.
578  *
579  * NOTE:  This assumes that the DBClearSet routine does not alter the
580  *        pointers between elements in the set (which is currently true),
581  *        and must be changed it this does not hold.
582  */
583 SHCopy()
584 {
585     register ELT *e1, *e2;
586     register POINT *p1, *p2;
587     float xmat[3][2];
588 
589     if (DBNullelt(cset)) {
590 	error(noset);
591 	return;
592     }
593 
594     if (SEQ < 2) {      /* not enough points */
595 	error(nopnt);
596 	return;
597     }
598 
599     UNForget();
600 
601     p1 = POINTLIST;
602     p2 = PTNextPoint(POINTLIST);
603 
604     while (!Nullpoint(p2)) {
605 	xmat[0][0] = xmat[1][1] = 1;    /* set up translation matrix */
606 	xmat[1][0] = xmat[0][1] = 0;
607 	xmat[2][0] = p2->x - p1->x;
608 	xmat[2][1] = p2->y - p1->y;
609 	e1 = cset;
610 
611 	DISClearSetDisplay();
612 	DBClearSet();    /* Dependent on Clearset preserving pointers */
613 
614 	while (!DBNullelt(e1)) {
615 	    e2 = DBCopy(e1, xmat, &PICTURE);
616 	    DISScreenAdd(e2, pixmask | csetmask);
617 	    DBAddSet(e2);
618 	    e1 = DBNextofSet(e1);
619 	}
620 
621 	p1 = p2;
622 	p2 = PTNextPoint(p2);
623     }
624 
625     CP();
626     CHANGED = TRUE;
627 }  /* end SHCopy */
628 
629 
630 /*
631  * This routine rotates the elements in the current set as defined
632  * by points.  The rotation is accomplished by defining a transformation
633  * matrix and calling DBXform.
634  */
635 SHRotate()
636 {
637     register ELT *elt;
638     register POINT *p1, *p2;
639     POINT pos;
640     float xmat[3][2], angle, s, c;
641 
642     if (DBNullelt(cset)) {
643 	error(noset);
644 	return;
645     }
646 
647     if (SEQ < 3) {      /* not enough points */
648 	error(nopnt);
649 	return;
650     }
651 
652     UNForget();
653 
654     p1 = PTNextPoint(POINTLIST);  /* calculate rotation angle */
655     p2 = PTNextPoint(p1);
656     angle = (float) atan2((p2->x - POINTLIST->x), (p2->y - POINTLIST->y)) -
657 	    (float) atan2((p1->x - POINTLIST->x), (p1->y - POINTLIST->y));
658     s = (float) sin(angle);
659     c = (float) cos(angle);
660 
661     /* Define transformation matrix to translate set to origin, rotate,
662       and translate back. */
663 
664     xmat[0][0] = c;
665     xmat[0][1] = -s;
666     xmat[1][0] = s;
667     xmat[1][1] = c;
668     xmat[2][0] = (-c) * POINTLIST->x - s * POINTLIST->y + POINTLIST->x;
669     xmat[2][1] = (-c) * POINTLIST->y + s * POINTLIST->x + POINTLIST->y;
670 
671     elt = cset;
672     /* DISClearSetDisplay(); */
673 
674     fasterase();
675 
676     while (!DBNullelt(elt)) {
677 	/* DISScreenErase(elt, pixmask); */
678 	TxMsgOK();
679 	DBXform(elt, xmat, &PICTURE);
680 
681 	if (TEXT(elt->type)) {
682 	    GRSetTextPos(elt->textpt, elt->type, elt->brushf, elt->size,
683 						elt->ptlist, &pos);
684 	    elt->ptlist = PTMakeTextPoints(elt->textpt, elt->brushf, elt->size,
685 						elt->ptlist, &pos);
686 	}
687 
688 	DISScreenAdd(elt, pixmask | csetmask);
689 	elt = DBNextofSet(elt);
690     }
691 
692     CP();
693     CHANGED = TRUE;
694 }  /* end SHRotate */
695 
696 
697 /*
698  * This routine scales the elements in the current set as defined
699  * by points.  The scaling is accomplished by defining a transformation
700  * matrix and calling DBXform.
701  */
702 SHScale()
703 {
704     register ELT *elt;
705     register POINT *p1, *p2;
706     POINT pos;
707     float xmat[3][2], d1, d2, scalex, scaley;
708 
709     if (DBNullelt(cset)) {
710 	error(noset);
711 	return;
712     }
713 
714     if (SEQ < 3) {      /* not enough points */
715 	error(nopnt);
716 	return;
717     }
718 
719     UNForget();
720 
721     p1 = PTNextPoint(POINTLIST);
722     p2 = PTNextPoint(p1);
723     d1 = sqrt(pow((p1->x - POINTLIST->x), 2.0) +
724 	      pow((p1->y - POINTLIST->y), 2.0));
725     d2 = sqrt( pow((p2->x - POINTLIST->x), 2.0) +
726 	      pow((p2->y - POINTLIST->y), 2.0));
727 
728     if (d1 == 0) {
729 	error("infinite scale");
730 	return;
731     }
732 
733     scalex = scaley = d2 / d1;
734 
735     /* create transformation matrix to translate set to origin,
736        performaing the scaling and translating back */
737 
738     xmat[0][0] = scalex;
739     xmat[1][1] = scaley;
740     xmat[1][0] = xmat[0][1] = 0;
741     xmat[2][0] = - POINTLIST->x * (scalex - 1.0);
742     xmat[2][1] = - POINTLIST->y * (scaley - 1.0);
743 
744     elt = cset;
745     fasterase();
746     /* DISClearSetDisplay(); */
747 
748     while (!DBNullelt(elt)) {
749 	/* DISScreenErase(elt, pixmask); */
750 	TxMsgOK();
751 	DBXform(elt, xmat, &PICTURE);
752 
753 	if (TEXT(elt->type)) {
754 	    GRSetTextPos(elt->textpt, elt->type, elt->brushf,
755 					    elt->size, elt->ptlist, &pos);
756 	    elt->ptlist = PTMakeTextPoints(elt->textpt, elt->brushf,
757 					    elt->size, elt->ptlist, &pos);
758 	}
759 
760 	DISScreenAdd(elt, pixmask | csetmask);
761 	elt = DBNextofSet(elt);
762     }
763 
764     CP();
765     CHANGED = TRUE;
766 }  /* end SHScale */
767 
768 
769 /*
770  * This routine redraws the graphics screen by clearing the screen ,
771  * redisplaying the menu and adding each element back to the display.
772  */
773 SHUpdate()
774 {
775     register ELT *e1;
776     register POINT *plist;
777     POINT pos;
778     register i;
779 
780     GRClear(pixmask | csetmask);
781 
782     if (Gridon)
783 	GRDisplayGrid();
784 
785     e1 = PICTURE;
786     while (!DBNullelt(e1)) {
787 	if (DBInCset(e1))
788 	    DISScreenAdd(e1, pixmask | csetmask);
789 	else
790 	    DISScreenAdd(e1, pixmask);
791 	e1 = DBNextElt(e1);
792     }
793 
794     CsetOn = 1;
795 
796     i = 0;
797     plist = POINTLIST;
798     while (!Nullpoint(plist)) {
799 	GRDisplayPoint(plist->x, plist->y, i++);
800 	plist = PTNextPoint(plist);
801     }
802 }  /* end SHUpdate */
803 
804 
805 /*
806  * This local routine stores the current set in the specified
807  * user symbol.
808  */
809 static
810 savemen(sym)
811 register sym;
812 {
813     register ELT *elt;
814     float xmat[3][2];
815 
816     xmat[0][0] = xmat[1][1] = 1;	/* set up copy transformation */
817     xmat[0][1] = xmat[1][0] = 0;	/* matrix for no transformation */
818     xmat[2][0] = xmat[2][1] = 0;
819 
820     while (!DBNullelt(MEN[sym])) {	/* clear out existing symbols */
821 	elt = DBNextElt(MEN[sym]);
822 	DBClearElt(MEN[sym]);
823 	MEN[sym] = elt;
824     }
825 
826     elt = cset;				/* copy current set to symbol */
827 
828     while (!DBNullelt(elt)) {
829 	(void) DBCopy(elt, xmat, &(MEN[sym]));
830 	elt = DBNextofSet(elt);
831     }
832 
833     if (SEQ == 0) {			/* no positioning points */
834 	MENPOINT[sym].x = 0;
835 	MENPOINT[sym].y = 0;
836     }
837     else {
838 	MENPOINT[sym].x = POINTLIST->x;
839 	MENPOINT[sym].y = POINTLIST->y;
840     }
841 
842     if (!DBNullelt(MEN[sym]))
843 	MNHighLt(HiBuffer[sym]);
844     else
845 	MNUnHighLt(HiBuffer[sym]);
846 
847     CP();
848     CHANGED = TRUE;
849 }  /* end savemen */
850 
851 
852 /*
853  * This routine saves the current set in user symbol 1 by
854  * calling savemen.
855  */
856 SHSave1()
857 {
858     savemen(0);
859 }
860 
861 
862 /*
863  * This routine saves the current set in user symbol 2 by
864  * calling savemen.
865  */
866 SHSave2()
867 {
868     savemen(1);
869 }
870 
871 
872 /*
873  * This routine saves the current set in user symbol 3 by
874  * calling savemen.
875  */
876 SHSave3()
877 {
878     savemen(2);
879 }
880 
881 
882 /*
883  * This routine saves the current set in user symbol 4 by
884  * calling savemen.
885  */
886 SHSave4()
887 {
888     savemen(3);
889 }
890 
891 
892 /*
893  * This routine creates and displays a rectangle whose diagonal is
894  * defined by two points.  The routine uses the coordinates of these
895  * points to define a VECTOR element with the appropriate vertices.
896  */
897 SHBox()
898 {
899     register POINT *p1, *p2;
900     register ELT *e1;
901     POINT *plist;
902     char *txt;
903 
904     if (SEQ < 2) {
905 	error("not enough points");
906 	return;
907     }
908 
909     UNForget();
910 
911     p1 = POINTLIST;
912     p2 = PTNextPoint(p1);
913     plist = PTInit();   /* create points for vector elements which define
914 			   the rectangle */
915     (void) PTMakePoint(p1->x, p1->y, &plist);
916     (void) PTMakePoint(p1->x, p2->y, &plist);
917     (void) PTMakePoint(p2->x, p2->y, &plist);
918     (void) PTMakePoint(p2->x, p1->y, &plist);
919     (void) PTMakePoint(p1->x, p1->y, &plist);   /* close rectangle */
920     txt = malloc(1);
921     *txt = '\0';
922 
923     DISClearSetDisplay();
924     DBClearSet();
925     e1 = DBCreateElt(VECTOR, plist, CBRUSH, 0, txt, &PICTURE);
926     DISScreenAdd(e1, pixmask | csetmask);
927     DBAddSet(e1);
928 
929     CP();
930     CHANGED = TRUE;
931 }  /* end SHBox */
932 
933 
934 /*
935  * This routine draws arrow heads by 'copying' the arrow head template
936  * into the picture appropriately transformed.
937  */
938 SHArrow()
939 {
940     register ELT *e1;
941     POINT p1;
942     register POINT *p2;
943     float xmat[3][2], angle, s, c;
944 
945     if (SEQ < 2) {      /* not enough points */
946 	error(nopnt);
947 	return;
948     }
949 
950     UNForget();
951 
952     p1.x = POINTLIST->x - 1;
953     p1.y = POINTLIST->y;
954     p2 = PTNextPoint(POINTLIST);
955     angle = (float) atan2((p2->x - POINTLIST->x),(p2->y - POINTLIST->y)) -
956 	    (float) atan2((p1.x - POINTLIST->x),(p1.y - POINTLIST->y));
957     s = (float) sin(angle);
958     c = (float) cos(angle);
959 
960    /* Define transformation matrix to translate element from origin
961       and rotate. */
962 
963     xmat[0][0] = c;
964     xmat[0][1] = -s;
965     xmat[1][0] = s;
966     xmat[1][1] = c;
967     xmat[2][0] = POINTLIST->x;
968     xmat[2][1] = POINTLIST->y;
969 
970     DISClearSetDisplay();	/* the new current set */
971     DBClearSet();		/* clear old set in preparation to make */
972     arhead.brushf = CBRUSH;
973     e1 = DBCopy(&arhead, xmat, &PICTURE);
974     DISScreenAdd(e1, pixmask | csetmask);
975     DBAddSet(e1);
976 
977     CP();
978     CHANGED = TRUE;
979 }  /* end SHArrow */
980 
981 /*
982  * Turn off any "showpoints" and erase the current set by XORing
983  * the cset pixrect with the picture.  ONLY to be used when the
984  * current set will be redrawn immediately afterwards.
985  */
986 fasterase()
987 {
988     CSP();			/* clear show points */
989 
990     GRCurrentSetOff();		/* erase current set */
991     CsetOn = 1;			/* ON, the current must be redrawn */
992     GRClear(csetmask);
993 }
994