1 
2 /* +-------------------------------------------------------------------+ */
3 /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
4 /* |                                                                   | */
5 /* | Permission to use, copy, modify, and to distribute this software  | */
6 /* | and its documentation for any purpose is hereby granted without   | */
7 /* | fee, provided that the above copyright notice appear in all       | */
8 /* | copies and that both that copyright notice and this permission    | */
9 /* | notice appear in supporting documentation.  There is no           | */
10 /* | representations about the suitability of this software for        | */
11 /* | any purpose.  this software is provided "as is" without express   | */
12 /* | or implied warranty.                                              | */
13 /* |                                                                   | */
14 /* +-------------------------------------------------------------------+ */
15 
16 /* $Id: polyOp.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */
17 
18 #ifdef __VMS
19 #define XtDisplay XTDISPLAY
20 #define XtWindow XTWINDOW
21 #endif
22 
23 #include <stdlib.h>
24 #include <math.h>
25 #include <X11/Intrinsic.h>
26 #include <X11/StringDefs.h>
27 #include <X11/cursorfont.h>
28 #include "PaintP.h"
29 #include "xpaint.h"
30 #include "misc.h"
31 #include "Paint.h"
32 #include "ops.h"
33 
34 #define	REGION		0x4
35 #define	FILL		0x2
36 #define	POLY		0x1
37 #define IsRegion(x)	(x & REGION)
38 #define IsPoly(x)	(x & POLY)
39 #define	IsFill(x)	(x & FILL)
40 
41 typedef struct {
42     int flag;
43     int npoints, start;
44     XPoint * points;
45     XPoint * realpts;
46     /*
47     **  Borrowed from my info structure.
48      */
49     GC lgc, fgc, gcx;
50     Widget widget;
51     Pixmap pixmap;
52     Boolean isFat;
53 } LocalInfo;
54 
55 static int MAXPOINTS = 30;
56 static int polygonType = 1;
57 static int polygonSides = 5;
58 static double polygonRatio = 0.381966; /* (3-sqrt(5))/2 for a perfect star */
59 
Vertices(XPoint * xp)60 void Vertices(XPoint *xp)
61 {
62     int i, num, ex, ey;
63     double c, s;
64 
65     /* Draw vertices of regular/starlike polygon
66        with center xp[0] and initial vertex point xp[1] */
67 
68     ex = xp[1].x - xp[0].x;
69     ey = xp[1].y - xp[0].y;
70 
71     num = (polygonType-1)*polygonSides;
72 
73     for (i=0; i<=num; i++) {
74         s = 2*i*M_PI/num;
75         c = cos(s);
76         s = sin(s);
77         if (polygonType == 3 && (i&1)) {
78 	    c *= polygonRatio;
79             s *= polygonRatio;
80 	}
81         xp[i+2].x = xp[0].x + (int)(c*ex - s*ey + 0.5);
82         xp[i+2].y = xp[0].y + (int)(s*ex + c*ey + 0.5);
83     }
84 }
85 
86 void
CreatePolygonalRegion(Widget w,XPoint * xp,int n)87 CreatePolygonalRegion(Widget w, XPoint *xp, int n)
88 {
89     Display *dpy;
90     XRectangle rect;
91     int i, xmin, ymin, xmax, ymax, width, height;
92     Pixmap mask;
93     GC gc;
94 
95     dpy = XtDisplay(w);
96 
97     /* Create region */
98     XtVaGetValues(w, XtNdrawWidth, &width,
99                      XtNdrawHeight, &height, NULL);
100     xmin = width;
101     ymin = height;
102     xmax = 0;
103     ymax = 0;
104 
105     /* n = 0 is special polygonal type */
106     if (n == 0) {
107         Vertices(xp);
108         xp += 2;
109         n = (polygonType-1) * polygonSides + 1;
110     }
111 
112     for (i=0; i<n; i++) {
113         if (xp[i].x<xmin) xmin = xp[i].x;
114         if (xp[i].y<ymin) ymin = xp[i].y;
115         if (xp[i].x>xmax) xmax = xp[i].x;
116         if (xp[i].y>ymax) ymax = xp[i].y;
117     }
118     if (xmin<0) xmin = 0;
119     if (ymin<0) ymin = 0;
120     if (xmax>=width) xmax = width-1;
121     if (ymax>=height) ymax = height-1;
122 
123     rect.x = xmin;
124     rect.y = ymin;
125     rect.width = xmax-xmin+1;
126     rect.height = ymax-ymin+1;
127     mask = XCreatePixmap(XtDisplay(w), XtWindow(w), rect.width, rect.height, 1);
128     gc = XCreateGC(XtDisplay(w), mask, 0, 0);
129     XSetFunction(XtDisplay(w), gc, GXclear);
130     XFillRectangle(XtDisplay(w), mask, gc, 0, 0,
131                    rect.width, rect.height);
132     XSetFunction(XtDisplay(w), gc, GXset);
133     for (i=0; i<n; i++) {
134          xp[i].x -= xmin;
135          xp[i].y -= ymin;
136     }
137     XFillPolygon(XtDisplay(w), mask, gc, xp, n, Complex, CoordModeOrigin);
138 
139     XFreeGC(XtDisplay(w), gc);
140     if (xmax==xmin || ymax==ymin) {
141         PwRegionFinish(w, True);
142         return;
143     }
144     if (SelectGetCutMode() != 0 && !chromaCut(w, &rect, &mask)) {
145         PwRegionFinish(w, True);
146         return;
147     }
148     PwRegionSet(w, &rect, None, mask);
149 }
150 
151 static Boolean
SpecialPolygon(LocalInfo * l)152 SpecialPolygon(LocalInfo * l)
153 {
154     if (l->flag && polygonType >= 2) return True;
155     return False;
156 }
157 
158 static void
contour(LocalInfo * l,Drawable d,GC gc)159 contour(LocalInfo * l, Drawable d, GC gc)
160 {
161     XPoint * xp;
162     int i, n;
163 
164     if (!d || l->npoints <= 1)
165       return;
166 
167     if (d == l->pixmap)
168         xp = l->realpts;
169     else
170         xp = l->points;
171 
172     if (SpecialPolygon(l)) {
173         Vertices(xp);
174         xp += 2;
175         n = (polygonType-1) * polygonSides + 1;
176     } else {
177         i = (l->start>=0)? l->start : 0;
178         xp += i;
179         n = l->npoints - i;
180     }
181 
182     XDrawLines(Global.display, d, gc, xp, n, CoordModeOrigin);
183 
184     if (d == l->pixmap)
185         for (i=0; i<n; i++) UndoGrow(l->widget, xp[i].x, xp[i].y);
186 }
187 
188 static void
finish(Widget w,LocalInfo * l,Boolean flag)189 finish(Widget w, LocalInfo * l, Boolean flag)
190 {
191     XPoint *xp;
192     int n;
193 
194     if (l->npoints <= 1) return;
195 
196     l->start = 0;
197     contour(l, XtWindow(w), l->gcx);
198 
199     if (IsRegion(l->flag)) {
200         if (SpecialPolygon(l))
201 	    n = 0;
202         else
203 	    n = l->npoints;
204         CreatePolygonalRegion(w, l->realpts, n);
205         l->npoints = -1;
206         return;
207     }
208 
209     if (IsFill(l->flag)) {
210         if (SpecialPolygon(l)) {
211             Vertices(l->realpts);
212             n = (polygonType-1) * polygonSides + 1;
213             xp = l->realpts + 2;
214 	} else {
215 	    xp = l->realpts;
216             n = l->npoints;
217 	}
218 	if (!l->isFat)
219 	    XFillPolygon(XtDisplay(w), XtWindow(w), l->fgc,
220 			 xp, n, Complex, CoordModeOrigin);
221 	XFillPolygon(XtDisplay(w), l->pixmap, l->fgc,
222 		     xp, n, Complex, CoordModeOrigin);
223     }
224 
225     if (IsPoly(l->flag) && polygonType == 1) {
226 	l->points[l->npoints] = l->points[0];
227 	l->realpts[l->npoints] = l->realpts[0];
228 	l->npoints++;
229     }
230 
231     SetCapAndJoin(w, l->lgc,
232                   ((Global.cap)?Global.cap-1:CapButt),
233                   ((Global.join)?Global.join-1:JoinMiter));
234     if (!l->isFat)
235         contour(l, XtWindow(w), l->lgc);
236 
237     contour(l, l->pixmap, l->lgc);
238     PwUpdate(w, NULL, False);
239 
240     l->npoints = -1;
241 }
242 
243 static void
press(Widget w,LocalInfo * l,XButtonEvent * event,OpInfo * info)244 press(Widget w, LocalInfo * l, XButtonEvent * event, OpInfo * info)
245 {
246     PaintWidget pw = (PaintWidget) w;
247 
248     if (event->button == Button3) return;
249 
250     if (l->npoints<0 && IsRegion(l->flag)
251         && pw->paint.region.isVisible) {
252 	  l->npoints = -3;
253           return;
254     }
255 
256     if (event->button == Button2)
257         finish(w, l, True);
258 }
259 
260 static void
shift(Widget w,LocalInfo * l,KeySym keysym)261 shift(Widget w, LocalInfo * l, KeySym keysym)
262 {
263     int i, zoom;
264     l->start = 0;
265     contour(l, XtWindow(w), l->gcx);
266     XtVaGetValues(w, XtNzoom, &zoom, NULL);
267     switch (keysym) {
268         case XK_Up:
269             for (i=0; i<= l->npoints; i++) {
270   	        --l->realpts[i].y;
271 	        if (zoom>0) {
272 	            l->points[i].y -= zoom;
273 	        } else {
274 		    l->points[i].y = l->realpts[i].y / (-zoom);
275 		}
276 	    }
277 	    break;
278         case XK_Down:
279             for (i=0; i<= l->npoints; i++) {
280   	        ++l->realpts[i].y;
281 	        if (zoom>0) {
282 	            l->points[i].y += zoom;
283 	        } else {
284 		    l->points[i].y = l->realpts[i].y / (-zoom);
285 		}
286 	    }
287             break;
288         case XK_Left:
289             for (i=0; i<= l->npoints; i++) {
290   	        --l->realpts[i].x;
291 	        if (zoom>0) {
292 	            l->points[i].x -= zoom;
293 	        } else {
294 		    l->points[i].x = l->realpts[i].x / (-zoom);
295 		}
296 	    }
297             break;
298         case XK_Right:
299             for (i=0; i<= l->npoints; i++) {
300   	        ++l->realpts[i].x;
301 	        if (zoom>0) {
302 	            l->points[i].x += zoom;
303 	        } else {
304 		    l->points[i].x = l->realpts[i].x / (-zoom);
305 		}
306 	    }
307             break;
308     }
309     contour(l, XtWindow(w), l->gcx);
310 }
311 
312 static void
release(Widget w,LocalInfo * l,XButtonEvent * event,OpInfo * info)313 release(Widget w, LocalInfo * l, XButtonEvent * event, OpInfo * info)
314 {
315     PaintWidget pw = (PaintWidget) w;
316     KeySym keysym;
317     char buf[21];
318     int i;
319 
320     if (event->type == KeyRelease) {
321         i = XLookupString((XKeyEvent *)event, buf, sizeof(buf)-1, &keysym, NULL);
322 
323         if (keysym == XK_Escape) {
324             l->start = 0;
325             contour(l, XtWindow(w), l->gcx);
326             l->npoints = -1;
327       	}
328         else
329         if (keysym == XK_Return) finish(w, l, True);
330         else
331         if (keysym == XK_Right || keysym == XK_Left ||
332             keysym == XK_Up || keysym == XK_Down)
333             shift(w, l, keysym);
334         else
335         if (keysym == XK_Delete || keysym == XK_BackSpace) {
336 	    if (l->npoints >= 2) {
337                 l->start = l->npoints - 3;
338                 contour(l, XtWindow(w), l->gcx);
339                 --l->npoints;
340                 i = l->npoints-1;
341                 if (i >= 1) {
342 		    l->points[i] = l->points[l->npoints];
343 		    l->realpts[i] = l->realpts[l->npoints];
344                     contour(l, XtWindow(w), l->gcx);
345 		}
346 	    }
347             /* if just one point left, restart from scratch */
348             if (l->npoints <= 1)
349                 l->npoints = -1;
350         }
351         event->type = None;
352         return;
353     }
354 
355     if (event->type != ButtonRelease || event->button >= Button2) return;
356     /* now event = ButtonRelase and button = 1 */
357 
358     if (l->npoints == -2) {
359         l->npoints = -1;
360         return;
361     }
362     if (l->npoints == -3 && IsRegion(l->flag) &&
363         pw->paint.region.isVisible) {
364         PwRegionFinish(w, True);
365         pw->paint.region.isVisible = False;
366         l->npoints = -2;
367         return;
368     }
369 
370     if (info->surface == opPixmap && !l->pixmap) {
371         UndoStartPoint(w, info, info->x, info->y);
372         l->pixmap = info->drawable;
373         l->widget = w;
374     }
375 
376     if (l->npoints<0 && info->surface == opWindow) {
377 	l->npoints = 1;
378 	if (SpecialPolygon(l)) {
379 	    MAXPOINTS = (polygonType-1)*polygonSides + 3;
380 	    l->points =
381                 (XPoint *) realloc(l->points, MAXPOINTS*sizeof(XPoint));
382 	    l->realpts =
383                 (XPoint *) realloc(l->realpts, MAXPOINTS*sizeof(XPoint));
384 	}
385 	l->points[0].x = event->x;
386 	l->points[0].y = event->y;
387 	l->realpts[0].x = info->x;
388 	l->realpts[0].y = info->y;
389 
390 	l->isFat = info->isFat;
391 	l->fgc = info->second_gc;
392 	l->lgc = info->first_gc;
393     }
394 
395     if (info->surface == opPixmap) return;
396 
397     /*
398     **  else on the window.
399      */
400     if (l->npoints >= MAXPOINTS - 3) {
401         MAXPOINTS += 10;
402         l->points =
403            (XPoint *) realloc(l->points, MAXPOINTS*sizeof(XPoint));
404         l->realpts =
405            (XPoint *) realloc(l->realpts, MAXPOINTS*sizeof(XPoint));
406     }
407 
408     l->start = l->npoints - 2;
409     contour(l, XtWindow(w), l->gcx);
410 
411     /* merely center / vertex for polygonType >= 2 */
412     if (SpecialPolygon(l))
413         l->npoints = 1;
414 
415     l->points[l->npoints].x = event->x;
416     l->points[l->npoints].y = event->y;
417     l->realpts[l->npoints].x = info->x;
418     l->realpts[l->npoints].y = info->y;
419     l->npoints++;
420 
421     contour(l, XtWindow(w), l->gcx);
422 }
423 
424 static void
motion(Widget w,LocalInfo * l,XMotionEvent * event,OpInfo * info)425 motion(Widget w, LocalInfo * l, XMotionEvent * event, OpInfo * info)
426 {
427     int i;
428 
429     if (l->npoints <= 1)
430         return;
431 
432     l->start = l->npoints - 2;
433     contour(l, XtWindow(w), l->gcx);
434 
435     i = l->npoints - 1;
436     l->points[i].x = event->x;
437     l->points[i].y = event->y;
438     l->realpts[i].x = info->x;
439     l->realpts[i].y = info->y;
440 
441     contour(l, XtWindow(w), l->gcx);
442 }
443 
444 static
createLocalInfo()445 LocalInfo * createLocalInfo()
446 {
447     LocalInfo * l;
448     l = (LocalInfo *) xmalloc(sizeof(LocalInfo));
449     l->points = (XPoint *) xmalloc(MAXPOINTS*sizeof(XPoint));
450     l->realpts =   (XPoint *) xmalloc(MAXPOINTS*sizeof(XPoint));
451     l->pixmap = None;
452     l->npoints = -1;
453     l->start = 0;
454     return l;
455 }
456 
457 static
freeLocalInfo(LocalInfo * l)458 void freeLocalInfo(LocalInfo *l)
459 {
460     free((void *) l->points);
461     free((void *) l->realpts);
462     free((void *) l);
463 }
464 
465 /*
466 **  Those public functions
467  */
468 void *
PolygonAdd(Widget w)469 PolygonAdd(Widget w)
470 {
471     LocalInfo *l =
472 
473     l = (LocalInfo *) createLocalInfo();
474 
475     l->flag = POLY;
476     l->gcx = GetGCX(w);
477 
478     XtVaSetValues(w, XtNcompress, True, NULL);
479 
480     OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
481 		      (OpEventProc) press, l);
482     OpAddEventHandler(w, opWindow, PointerMotionMask, FALSE,
483 		      (OpEventProc) motion, l);
484     OpAddEventHandler(w, opWindow | opPixmap,
485                       KeyReleaseMask|ButtonReleaseMask, FALSE,
486 		      (OpEventProc) release, l);
487     SetCrossHairCursor(w);
488 
489     return l;
490 }
491 
492 void
PolygonRemove(Widget w,void * l)493 PolygonRemove(Widget w, void *l)
494 {
495     OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
496 			 (OpEventProc) press, l);
497     OpRemoveEventHandler(w, opWindow, PointerMotionMask, FALSE,
498 			 (OpEventProc) motion, l);
499     OpRemoveEventHandler(w, opWindow | opPixmap,
500                          KeyReleaseMask|ButtonReleaseMask, FALSE,
501 			 (OpEventProc) release, l);
502 
503     finish(w, (LocalInfo *) l, True);
504     freeLocalInfo((LocalInfo *) l);
505 }
506 
507 void *
FilledPolygonAdd(Widget w)508 FilledPolygonAdd(Widget w)
509 {
510     LocalInfo *l;
511 
512     l = (LocalInfo *) createLocalInfo();
513     l->flag = POLY | FILL;
514     l->gcx = GetGCX(w);
515 
516     XtVaSetValues(w, XtNcompress, True, NULL);
517 
518     OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
519 		      (OpEventProc) press, l);
520     OpAddEventHandler(w, opWindow, PointerMotionMask, FALSE,
521 		      (OpEventProc) motion, l);
522     OpAddEventHandler(w, opWindow | opPixmap,
523                       KeyReleaseMask|ButtonReleaseMask, FALSE,
524 		      (OpEventProc) release, l);
525     SetCrossHairCursor(w);
526 
527     return l;
528 }
529 
530 void
FilledPolygonRemove(Widget w,void * l)531 FilledPolygonRemove(Widget w, void *l)
532 {
533     OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
534 			 (OpEventProc) press, l);
535     OpRemoveEventHandler(w, opWindow, PointerMotionMask, FALSE,
536 			 (OpEventProc) motion, l);
537     OpRemoveEventHandler(w, opWindow | opPixmap,
538                          KeyReleaseMask|ButtonReleaseMask, FALSE,
539 			 (OpEventProc) release, l);
540 
541     finish(w, (LocalInfo *) l, True);
542     freeLocalInfo((LocalInfo *) l);
543 }
544 
545 void *
SelectPolygonAdd(Widget w)546 SelectPolygonAdd(Widget w)
547 {
548     LocalInfo *l;
549 
550     l = (LocalInfo *) createLocalInfo();
551     l->flag = POLY | REGION;
552     l->gcx = GetGCX(w);
553 
554     XtVaSetValues(w, XtNcompress, True, NULL);
555 
556     OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
557 		      (OpEventProc) press, l);
558     OpAddEventHandler(w, opWindow, PointerMotionMask, FALSE,
559 		      (OpEventProc) motion, l);
560     OpAddEventHandler(w, opWindow | opPixmap,
561                       KeyReleaseMask|ButtonReleaseMask, FALSE,
562 		      (OpEventProc) release, l);
563 
564     SetCrossHairCursor(w);
565 
566     return l;
567 }
568 
569 void
SelectPolygonRemove(Widget w,void * l)570 SelectPolygonRemove(Widget w, void *l)
571 {
572     OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
573 			 (OpEventProc) press, l);
574     OpRemoveEventHandler(w, opWindow, PointerMotionMask, FALSE,
575 			 (OpEventProc) motion, l);
576     OpRemoveEventHandler(w, opWindow | opPixmap,
577                          KeyReleaseMask|ButtonReleaseMask, FALSE,
578 			 (OpEventProc) release, l);
579 
580     finish(w, (LocalInfo *) l, True);
581     freeLocalInfo((LocalInfo *) l);
582 }
583 
584 void *
BrokenlineAdd(Widget w)585 BrokenlineAdd(Widget w)
586 {
587     LocalInfo *l;
588 
589     l = createLocalInfo();
590     l->flag = 0;
591     l->gcx = GetGCX(w);
592 
593     XtVaSetValues(w, XtNcompress, True, NULL);
594 
595     OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
596 		      (OpEventProc) press, l);
597     OpAddEventHandler(w, opWindow, PointerMotionMask, FALSE,
598 		      (OpEventProc) motion, l);
599     OpAddEventHandler(w, opWindow | opPixmap,
600                       KeyReleaseMask|ButtonReleaseMask, FALSE,
601 		      (OpEventProc) release, l);
602     SetCrossHairCursor(w);
603 
604     return l;
605 }
606 
607 void
BrokenlineRemove(Widget w,void * l)608 BrokenlineRemove(Widget w, void *l)
609 {
610     OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE,
611 			 (OpEventProc) press, l);
612     OpRemoveEventHandler(w, opWindow, PointerMotionMask, FALSE,
613 			 (OpEventProc) motion, l);
614     OpRemoveEventHandler(w, opWindow | opPixmap,
615                          KeyReleaseMask|ButtonReleaseMask, FALSE,
616 			 (OpEventProc) release, l);
617 
618     finish(w, (LocalInfo *) l, True);
619     freeLocalInfo((LocalInfo *) l);
620 }
621 
622 /*
623 **  Those public functions
624 */
625 void
PolygonSetParameters(int t,int s,double a)626 PolygonSetParameters(int t, int s, double a)
627 {
628     if (t)
629        polygonType = t;
630     else {
631        polygonSides = s;
632        if (a==1.0)
633 	   polygonRatio = 1/(2*cos(M_PI/s)+1.0);
634        else
635            polygonRatio = a;
636     }
637 }
638 
639 void
PolygonGetParameters(int * t,int * s,double * a)640 PolygonGetParameters(int *t, int *s, double *a)
641 {
642   *t = polygonType;
643   *s = polygonSides;
644   *a = polygonRatio;
645 }
646