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