1 /* Utility functions used */
2 /* COPYRIGHT (C) 2000 THE VICTORIA UNIVERSITY OF MANCHESTER and John Levon
3 * This program is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by the Free
5 * Software Foundation; either version 2 of the License, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15 * Place - Suite 330, Boston, MA 02111-1307, USA.
16 */
17 /*
18 * $Log: util.c,v $
19 * Revision 1.3 2000/12/17 00:57:43 moz
20 * examples, filled open splines, highlight_objects
21 *
22 * Revision 1.2 2000/12/06 20:56:07 moz
23 * GPL stuff.
24 *
25 * Revision 1.1.1.1 2000/08/21 01:05:31 moz
26 *
27 *
28 * Revision 1.1.1.1 2000/07/19 22:45:31 moz
29 * CVS Import
30 *
31 * Revision 1.36 2000/05/04 03:38:35 moz
32 * Got font faking working fairly sensibly.
33 *
34 * Revision 1.35 2000/05/04 02:27:01 moz
35 * Fix font scaling to some degree.
36 *
37 * Revision 1.34 2000/03/13 02:46:53 moz
38 * redraw_object fixes for lw.
39 *
40 * Revision 1.33 2000/03/09 23:43:52 moz
41 * No end styles for closed splines.
42 *
43 * Revision 1.32 2000/03/09 23:29:40 moz
44 * Don't allow joinstyle for splines/arcs.
45 *
46 * Revision 1.31 2000/03/06 02:28:49 moz
47 * Compile fixes.
48 *
49 * Revision 1.30 2000/03/06 01:06:18 moz
50 * Show arrow button in spline icon.
51 *
52 * Revision 1.29 2000/03/04 20:49:24 moz
53 * Fonts should be scaled by screen_ppi.
54 *
55 * Revision 1.28 2000/02/25 00:16:22 moz
56 * Set node fillcolour and fillstyle.
57 *
58 * Revision 1.27 2000/02/04 01:55:26 moz
59 * switch_icons(): don't rejig all icons if resulting from
60 * object selection.
61 *
62 * Revision 1.26 2000/02/01 16:52:20 moz
63 * switch_icons() fixes.
64 *
65 * Revision 1.25 2000/01/28 17:32:12 moz
66 * Inline cosround and sinround.
67 *
68 * Revision 1.24 2000/01/25 01:37:36 moz
69 * Don't set a border.
70 *
71 * Revision 1.23 2000/01/21 11:50:55 moz
72 * Removed dead comment.
73 *
74 * Revision 1.22 1999/11/15 23:01:21 moz
75 * Back out window groups change.
76 *
77 * Revision 1.21 1999/11/15 02:00:55 moz
78 * Rounded sin and cos for rotation.
79 * Name change.
80 * Set window group hint.
81 *
82 * Revision 1.20 1999/07/04 22:11:34 moz
83 * Place view icons correctly for more than one view.
84 *
85 * Revision 1.19 1999/06/30 15:32:59 moz
86 * Updated the view icon sizes in switch_icons().
87 *
88 * Revision 1.18 1999/05/22 02:49:08 moz
89 * Fixes for redrawing compounds.
90 *
91 * Revision 1.17 1999/05/19 17:06:43 moz
92 * 1.0 Checkin.
93 *
94 * Revision 1.16 1999/05/04 15:23:51 moz
95 * Moved snap() from obsolete grid.c
96 *
97 * Revision 1.15 1999/05/04 14:40:58 moz
98 * Allow arrow dialog for arc ellipses.
99 *
100 * Revision 1.14 1999/05/01 23:21:08 moz
101 * Changes to dynamic view_icon_window.
102 *
103 * Revision 1.13 1999/04/29 01:35:10 moz
104 * Make sure we don't try to switch icons to a user-defined colour.
105 *
106 * Revision 1.12 1999/04/29 00:26:17 moz
107 * Removed node box icon.
108 *
109 * Revision 1.11 1999/04/29 00:12:55 moz
110 * Disabled NO_INTERSECT in ellipse_box as it's broken.
111 *
112 * Revision 1.10 1999/04/28 03:29:39 moz
113 * coredump fix in switch_icons.
114 *
115 * Revision 1.9 1999/04/27 23:55:25 moz
116 * switch_icons() extended to set view icons from selected object.
117 *
118 * Revision 1.8 1999/04/27 18:48:19 moz
119 * Enable colour change for TEXT objects.
120 *
121 * Revision 1.7 1999/04/27 16:00:51 moz
122 * Flip angles in x and y axis.
123 *
124 * Revision 1.6 1999/04/27 04:24:30 moz
125 * draw_view parameters change.
126 *
127 * Revision 1.5 1999/04/27 04:12:44 moz
128 * Remove extraneous variable.
129 *
130 * Revision 1.4 1999/04/23 00:40:13 moz
131 * redraw_view_window change.
132 *
133 * Revision 1.3 1999/04/04 01:47:03 moz
134 * Merge multiple store redraw requests before sending.
135 *
136 * Revision 1.2 1999/04/03 23:13:51 moz
137 * Don't show Set Spline icon when in Pointer mode (editing).
138 *
139 * Revision 1.1 1999/03/30 00:07:04 moz
140 * Initial revision
141 *
142 */
143
144 #include <stdlib.h>
145 #include <pwd.h>
146 #include <sys/types.h>
147 #include <unistd.h>
148 #include <string.h>
149 #include <math.h>
150
151 #include "include/figurine.h"
152 #include "include/extern.h"
153
154 /* for use of store_redraw_object */
155 static Boolean storedflag = FALSE;
156 static long slw = 0;
157 static long sjs = JoinRound;
158 static long sfarrow_w = 0;
159 static long sfarrow_h = 0;
160 static long sbarrow_w = 0;
161 static long sbarrow_h = 0;
162 static VRegion sbbox;
163 static Boolean scompound=FALSE;
164
165 void
free_list(void * v)166 free_list(void *v)
167 {
168 freelist = add_to_list(freelist,0,0,v);
169 }
170
171 /* set X clipping region */
172 inline void
set_clip(GC agc,int x,int y,int w,int h)173 set_clip(GC agc, int x, int y, int w, int h)
174 {
175 static XRectangle rectangles[1];
176
177 rectangles[0].x = 0;
178 rectangles[0].y = 0;
179 rectangles[0].width = w;
180 rectangles[0].height = h;
181
182 XSetClipRectangles(display,agc, x, y, rectangles, 1, Unsorted);
183 }
184
185 void
set_clip_v(GC agc,View * v,VRegion bbox)186 set_clip_v(GC agc, View *v, VRegion bbox)
187 {
188 XRectangle rectangles[1];
189
190 rectangles[0].x = 0;
191 rectangles[0].y = 0;
192 rectangles[0].width = XD2P(bbox.x2,v)-XD2P(bbox.x1,v)+1;
193 rectangles[0].height = YD2P(bbox.y2,v)-YD2P(bbox.y1,v)+1;
194 XSetClipRectangles(display,agc, XD2P(bbox.x1,v), YD2P(bbox.y1,v), rectangles, 1, Unsorted);
195 }
196
197 /* determine user's $HOME */
198 void
home_directory(char * result)199 home_directory(char *result)
200 {
201 uid_t uid;
202 struct passwd *password = NULL;
203 char *tmp;
204
205 if ((tmp=getenv("HOME"))!=NULL)
206 {
207 strcpy(result, tmp);
208 return;
209 }
210 else
211 {
212 if ((tmp=getenv("USER"))!=NULL)
213 {
214 password = getpwnam(tmp);
215 }
216 else
217 {
218 uid = getuid();
219 password = getpwuid(uid);
220 };
221 };
222
223 if (password)
224 {
225 strcpy(result,password->pw_dir);
226 return;
227 };
228
229 *result = '\0';
230 }
231
232 inline Boolean
is_in_box(int px,int py,int x,int y,int w,int h)233 is_in_box(int px, int py, int x, int y, int w, int h)
234 {
235 return (px>=x && px <=x+w && py>=y && py<=y+h);
236 }
237
238 inline Boolean
is_in_bbox(int px,int py,int x1,int y1,int x2,int y2)239 is_in_bbox(int px, int py, int x1, int y1, int x2, int y2)
240 {
241 return (px>=x1 && px<=x2 && py>=y1 && py<=y2);
242 }
243
244 inline Boolean
is_in_object(long px,long py,Object * ob)245 is_in_object(long px, long py, Object *ob)
246 {
247 return (px>=ob->bbox.x1 && px<=ob->bbox.x2 && py>=ob->bbox.y1 && py<=ob->bbox.y2);
248 }
249
250 Boolean
is_in_object_p(View * v,long x,long y,Object * ob)251 is_in_object_p(View *v, long x, long y, Object *ob)
252 {
253 return is_in_object(XP2D(x,v), YP2D(y,v), ob);
254 }
255
256 /* usage is_contained(outer_box, inner_box) */
257 inline Boolean
is_contained(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)258 is_contained(int x1, int y1, int x2, int y2,int x3, int y3, int x4, int y4)
259 {
260 return (is_in_bbox(x3,y3,x1,y1,x2,y2) && is_in_bbox(x4,y4,x1,y1,x2,y2));
261 }
262
263 /* usage bbox = merge_boxes(box1, box2) */
264 /* box1 box2 must both be normalised */
265 VRegion
merge_boxes(VRegion b1,VRegion b2)266 merge_boxes(VRegion b1, VRegion b2)
267 {
268 VRegion r;
269
270 /* this works because b1,b2 are normalised, right ? */
271 r.x1 = min(b1.x1, b2.x1);
272 r.y1 = min(b1.y1, b2.y1);
273 r.x2 = max(b1.x2, b2.x2);
274 r.y2 = max(b1.y2, b2.y2);
275
276 return r;
277 }
278
279 inline Boolean
is_contained_v(VRegion a,VRegion b)280 is_contained_v(VRegion a, VRegion b)
281 {
282 return (is_in_bbox(b.x1,b.y1,a.x1,a.y1,a.x2,a.y2) && is_in_bbox(b.x2,b.y2,a.x1,a.y1,a.x2,a.y2));
283 }
284
285 /* determine whether two boxes intersect */
286 inline Boolean
intersects(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)287 intersects(int x1, int y1, int x2, int y2,int x3, int y3, int x4, int y4)
288 {
289 /* first test if a corner is in the other rectangle */
290 if (is_in_bbox(x1,y1,x3,y3,x4,y4))
291 return TRUE;
292
293 if (is_in_bbox(x2,y2,x3,y3,x4,y4))
294 return TRUE;
295
296 if (is_in_bbox(x1,y2,x3,y3,x4,y4))
297 return TRUE;
298
299 if (is_in_bbox(x2,y1,x3,y3,x4,y4))
300 return TRUE;
301
302 /* if contained, TRUE */
303
304 if (is_contained(x1,y1,x2,y2,x3,y3,x4,y4))
305 return TRUE;
306
307 if (is_contained(x3,y3,x4,y4,x1,y1,x2,y2))
308 return TRUE;
309
310 /* better double check for other rectangle */
311 if (is_in_bbox(x3,y3,x1,y1,x2,y2))
312 return TRUE;
313
314 if (is_in_bbox(x4,y4,x1,y1,x2,y2))
315 return TRUE;
316
317 if (is_in_bbox(x3,y4,x1,y1,x2,y2))
318 return TRUE;
319
320 if (is_in_bbox(x4,y3,x1,y1,x2,y2))
321 return TRUE;
322
323 /* if contained, TRUE */
324
325 if (((x3>=x1 && x3<=x2) || (x4>=x1 && x4<=x2)) && y3<=y1 && y4>=y2)
326 return TRUE;
327
328 if (((y3>=y1 && y3<=y2) || (y4>=y1 && y4<=y2)) && x3<=x1 && x4>=x2)
329 return TRUE;
330
331 return FALSE;
332 }
333
334 Boolean
intersects_v(VRegion v1,VRegion v2)335 intersects_v(VRegion v1, VRegion v2)
336 {
337 return intersects(v1.x1,v1.y1,v1.x2,v1.y2, v2.x1,v2.y1,v2.x2,v2.y2);
338 }
339
340 /* normalised rectangle !!! */
341 /* forces a point to be in the box x1,y1-x2,y2 */
342 void
force_in_box(int * xp,int * yp,int x1,int y1,int x2,int y2)343 force_in_box(int *xp, int *yp, int x1, int y1, int x2, int y2)
344 {
345 if (*xp < x1 || *xp > x2)
346 *xp = (x1+x2)/2;
347
348 if (*yp < y1 || *yp > y2)
349 *yp = (y1+y2)/2;
350
351 }
352
353 void
force_in_box_l(long * xp,long * yp,long x1,long y1,long x2,long y2)354 force_in_box_l(long *xp, long *yp, long x1, long y1, long x2, long y2)
355 {
356 *xp = max(x1,*xp);
357 *xp = min(x2,*xp);
358 *yp = max(y1,*yp);
359 *yp = min(y2,*yp);
360
361 }
362
363 /* simple equation of line */
364 signed long
line_x_at_y(long x1,long y1,long x2,long y2,long y)365 line_x_at_y(long x1, long y1, long x2, long y2, long y)
366 {
367 return (signed long)( ((((double)(x2-x1))/(y2-y1))*(y-y1))+x1);
368 }
369
370 /* simple equation of line */
371 signed long
line_y_at_x(long x1,long y1,long x2,long y2,long x)372 line_y_at_x(long x1, long y1, long x2, long y2, long x)
373 {
374 return (signed long)( ((((double)(y2-y1))/(x2-x1))*(x-x1))+y1);
375 }
376
377 /* rounded sin and cos useful for us*/
378 inline double
sinround(double angle)379 sinround(double angle)
380 {
381 double a=sin(angle);
382
383 if (between(a,-0.03,0.03))
384 return 0.0;
385
386 if (between(a,0.97,1.0))
387 return 1.0;
388
389 if (between(a,-1.0,-0.97))
390 return -1.0;
391
392 return a;
393 }
394
395 inline double
cosround(double angle)396 cosround(double angle)
397 {
398 double a=cos(angle);
399
400 if (between(a,-0.03,0.03))
401 return 0.0;
402
403 if (between(a,0.97,1.0))
404 return 1.0;
405
406 if (between(a,-1.0,-0.97))
407 return -1.0;
408
409 return a;
410 }
411
412 /* flip an angle in the x axis */
413 double
flip_angle_x(double angle)414 flip_angle_x(double angle)
415 {
416 double x,y;
417
418 x = - cosround(angle);
419 y = sinround (angle);
420
421 angle = atan2(y,x);
422
423 while (angle>2*PI)
424 angle -= 2*PI;
425
426 while (angle<0)
427 angle += 2*PI;
428
429 return angle;
430 }
431
432 /* flip an angle in the y axis */
433 double
flip_angle_y(double angle)434 flip_angle_y(double angle)
435 {
436 double x,y;
437
438 x = cosround(angle);
439 y = - sinround(angle);
440
441 angle = atan2(y,x);
442
443 while (angle>2*PI)
444 angle -= 2*PI;
445
446 while (angle<0)
447 angle += 2*PI;
448
449 return angle;
450 }
451
452 /* normalise a rectangle described by two points */
453 void
normalise_rectangle(long * x1,long * y1,long * x2,long * y2)454 normalise_rectangle(long *x1, long *y1, long *x2, long *y2)
455 {
456 long a;
457
458 if (*x1<=*x2 && *y1<=*y2) /* line from tl to br */
459 return;
460
461 if (*x1>=*x2 && *y1>=*y2) /* line from br to tl */
462 {
463 a = *x2;
464 *x2 = *x1;
465 *x1 = a;
466 a = *y2;
467 *y2 = *y1;
468 *y1 = a;
469 return;
470 };
471
472 if (*x1<=*x2 && *y1>=*y2) /* line from bl to tr */
473 {
474 a = *y1;
475 *y1 = *y2;
476 *y2 = a;
477 return;
478 };
479
480 if (*x1>=*x2 && *y1<=*y2) /* line from tr to bl */
481 {
482 a = *x1;
483 *x1 = *x2;
484 *x2 = a;
485 return;
486 };
487
488 }
489
490 /* send a redraw for a section */
491 void
send_redraw(WindowStruct * window,int x,int y,int w,int h)492 send_redraw(WindowStruct *window, int x, int y, int w, int h)
493 {
494 XEvent ev;
495
496 set_clip(ugc, x, y, w, h);
497 set_clip(handlegc, x, y, w, h);
498 set_clip(blackxorgc, x, y, w, h);
499 set_clip(dashgc, x, y, w, h);
500 set_clip(fillgc, x, y, w, h);
501
502 ev.type = Expose;
503 ev.xexpose.window = window->win;
504 ev.xexpose.x = x;
505 ev.xexpose.y = y;
506 ev.xexpose.width = w;
507 ev.xexpose.height = h;
508 ev.xexpose.count = 0;
509
510 XSendEvent(display, window->win, False, 0, &ev);
511 }
512
513 void
send_redraw_v(View * view,VRegion bb)514 send_redraw_v(View *view, VRegion bb)
515 {
516 int x1,y1,x2,y2;
517 List l;
518
519 l = view->doc->views;
520
521 while (l!=NULL)
522 {
523
524 x1 = XD2P(bb.x1,VIEW(l));
525 y1 = YD2P(bb.y1,VIEW(l));
526 x2 = XD2P(bb.x2,VIEW(l));
527 y2 = YD2P(bb.y2,VIEW(l));
528
529 if (intersects(x1,y1,x2,y2,0,0,(int)VIEW(l)->draw_window->w,(int)VIEW(l)->draw_window->h))
530 {
531 x1 = max(0,x1);
532 y1 = max(0,y1);
533 x2 = min(x2-x1,(int)VIEW(l)->draw_window->w);
534 y2 = min(y2-y1,(int)VIEW(l)->draw_window->h);
535
536 send_redraw(VIEW(l)->draw_window, x1, y1, x2+1, y2+1);
537 };
538 l = l->next;
539 };
540 }
541
542 /* store shape of first object */
543 void
store_redraw_object(Object * ob)544 store_redraw_object(Object *ob)
545 {
546 /* if we already have stored, but not sent, merge the requests */
547 if (storedflag)
548 sbbox = merge_boxes(sbbox,ob->bbox);
549 else
550 sbbox = ob->bbox;
551
552 if (ob->type!=COMPOUND)
553 {
554 slw = ob->lw;
555 sjs = ob->js;
556
557 if (ob->farrow!=NULL)
558 {
559 sfarrow_w = ob->farrow->w;
560 sfarrow_h = ob->farrow->h;
561 }
562 else
563 sfarrow_w = 0; /* checked for */
564
565 if (ob->barrow!=NULL)
566 {
567 sbarrow_w = ob->barrow->w;
568 sbarrow_h = ob->barrow->h;
569 }
570 else
571 sbarrow_w = 0; /* checked for */
572 }
573 else
574 scompound=TRUE;
575
576 storedflag = TRUE;
577 }
578
579 /* merge ob with value stored above */
580 /* and send redraw */
581 void
send_stored_redraw_object(View * view,Object * ob)582 send_stored_redraw_object(View *view, Object *ob)
583 {
584 VRegion bb;
585 VRegion sbb;
586 int x1,y1,x2,y2;
587 long v =0;
588 List l;
589 long lw;
590
591 storedflag = FALSE;
592 l = view->doc->views;
593
594 while (l!=NULL)
595 {
596 v = 0;
597 if (ob->type == POLYLINE || ob->type==POLYGON)
598 {
599 lw = I2D((((double)ob->lw)/80.0),VIEW(l));
600 /* if join miter, the point can be miles away from bb */
601 if (ob->js==JoinMiter)
602 lw = I2D((((double)ob->lw)/80.0),VIEW(l))*6;
603
604 if (ob->farrow!=NULL)
605 v = I2D((((double)max(ob->farrow->w,ob->farrow->h))/80.0),VIEW(l));
606
607 if (ob->barrow!=NULL)
608 v = max(v, I2D((((double)max(ob->barrow->w,ob->barrow->h))/80.0),VIEW(l)));
609
610 lw += v;
611 }
612 else
613 lw = 0;
614
615 bb.x1 = ob->bbox.x1-lw;
616 bb.y1 = ob->bbox.y1-lw;
617 bb.x2 = ob->bbox.x2+lw;
618 bb.y2 = ob->bbox.y2+lw;
619
620 /* account for handles in editing mode */
621 bb.x1 = bb.x1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
622 bb.y1 = bb.y1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
623 bb.x2 = bb.x2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
624 bb.y2 = bb.y2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
625
626 v = 0;
627 if (!scompound)
628 {
629 lw = I2D((((double)slw)/80.0),VIEW(l));
630 /* if join miter, the point can be miles away from bb */
631 if (sjs==JoinMiter)
632 lw = I2D((((double)slw)/80.0),VIEW(l))*6;
633
634 if (sfarrow_w!=0)
635 v = I2D((((double)max(sfarrow_w,sfarrow_h))/80.0),VIEW(l));
636 if (sbarrow_w!=0)
637 v = I2D((((double)max(sbarrow_w,sbarrow_h))/80.0),VIEW(l));
638
639 lw += v;
640 }
641 else
642 lw = I2D(1,VIEW(l));
643
644 sbb.x1 = sbbox.x1-lw;
645 sbb.y1 = sbbox.y1-lw;
646 sbb.x2 = sbbox.x2+lw;
647 sbb.y2 = sbbox.y2+lw;
648
649 /* account for handles in editing mode */
650 sbb.x1 = sbb.x1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
651 sbb.y1 = sbb.y1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
652 sbb.x2 = sbb.x2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
653 sbb.y2 = sbb.y2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
654
655 bb = merge_boxes(bb,sbb);
656
657 x1 = XD2P(bb.x1,VIEW(l));
658 y1 = YD2P(bb.y1,VIEW(l));
659 x2 = XD2P(bb.x2,VIEW(l));
660 y2 = YD2P(bb.y2,VIEW(l));
661
662 if (intersects(x1,y1,x2,y2,0,0,(int)VIEW(l)->draw_window->w,(int)VIEW(l)->draw_window->h))
663 {
664 x1 = max(0,x1);
665 y1 = max(0,y1);
666 x2 = min(x2-x1,(int)VIEW(l)->draw_window->w);
667 y2 = min(y2-y1,(int)VIEW(l)->draw_window->h);
668
669 send_redraw(VIEW(l)->draw_window, x1, y1, x2+1, y2+1);
670 };
671
672 l = l->next;
673 };
674 scompound=FALSE;
675 }
676
677 /* redraw the whole view window */
678 void
redraw_view_window(View * view)679 redraw_view_window(View *view)
680 {
681 XEvent xev;
682
683 /* remove queued redraw events on this window */
684 while (XCheckTypedWindowEvent(display, view->draw_window->win, Expose, &xev))
685 ;
686
687 draw_view(view,0,0,(long)view->draw_window->w,(long)view->draw_window->h);
688 }
689
690 /* redraw an object, taking account
691 of object properties */
692 void
send_redraw_object(View * view,Object * ob)693 send_redraw_object(View *view, Object *ob)
694 {
695 VRegion bb;
696 int x1,y1,x2,y2;
697 long v =0;
698 List l;
699 long lw=0;
700
701 l = view->doc->views;
702
703 while (l!=NULL)
704 {
705 if (ob->type == POLYLINE || ob->type==POLYGON)
706 {
707 lw = I2D((((double)ob->lw)/80.0),VIEW(l));
708 /* if join miter, the point can be miles away from bb */
709 if (ob->js==JoinMiter)
710 lw = I2D((((double)ob->lw)/80.0),VIEW(l))*6;
711
712 if (ob->farrow!=NULL)
713 v = I2D((((double)max(ob->farrow->w,ob->farrow->h))/80.0),VIEW(l));
714
715 if (ob->barrow!=NULL)
716 v = max(v, I2D((((double)max(ob->barrow->w,ob->barrow->h))/80.0),VIEW(l)));
717
718 lw += v;
719 }
720 else
721 lw = 0;
722
723 bb.x1 = ob->bbox.x1-lw;
724 bb.y1 = ob->bbox.y1-lw;
725 bb.x2 = ob->bbox.x2+lw;
726 bb.y2 = ob->bbox.y2+lw;
727
728 /* account for handles in editing mode */
729 bb.x1 = bb.x1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
730 bb.y1 = bb.y1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
731 bb.x2 = bb.x2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
732 bb.y2 = bb.y2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
733
734 x1 = XD2P(bb.x1,VIEW(l));
735 y1 = YD2P(bb.y1,VIEW(l));
736 x2 = XD2P(bb.x2,VIEW(l));
737 y2 = YD2P(bb.y2,VIEW(l));
738
739 if (intersects(x1,y1,x2,y2,0,0,(int)VIEW(l)->draw_window->w,(int)VIEW(l)->draw_window->h))
740 {
741 x1 = max(0,x1);
742 y1 = max(0,y1);
743 x2 = min(x2-x1,(int)VIEW(l)->draw_window->w);
744 y2 = min(y2-y1,(int)VIEW(l)->draw_window->h);
745
746 send_redraw(VIEW(l)->draw_window, x1, y1, x2+1, y2+1);
747 };
748 l = l->next;
749 };
750 }
751
752 /* configure a window */
753 void
send_configure(WindowStruct * window,int x,int y,int w,int h)754 send_configure(WindowStruct *window, int x, int y, int w, int h)
755 {
756 XEvent ev;
757
758 ev.type = ConfigureNotify;
759 ev.xconfigure.window = window->win;
760 ev.xconfigure.x = x;
761 ev.xconfigure.y = y;
762 ev.xconfigure.width = w;
763 ev.xconfigure.height = h;
764 ev.xconfigure.border_width = 0;
765 ev.xconfigure.above = None;
766 ev.xconfigure.override_redirect = FALSE;
767
768 XSendEvent(display, window->win, False, 0, &ev);
769 }
770
771 /* set a window cursor type */
772 void
set_window_cursor(Window win,int c)773 set_window_cursor(Window win, int c)
774 {
775 Cursor tc;
776 XSetWindowAttributes wattr;
777
778 tc = XCreateFontCursor(display,(uint)c);
779 wattr.cursor = tc;
780
781 XChangeWindowAttributes(display,win, CWCursor, &wattr);
782 }
783
784 /* create a normal window */
785 Boolean
create_window(WindowStruct * window,Window parent,List * list,int type,void * data)786 create_window(WindowStruct *window, Window parent, List *list, int type, void *data)
787 {
788 List templ;
789 XGCValues gcvalues;
790
791 XGetGCValues(display, window->gc, (GCForeground | GCBackground), &gcvalues);
792
793 window->win = XCreateSimpleWindow(display, parent,
794 window->x,window->y,window->w,window->h,
795 0, gcvalues.foreground,
796 gcvalues.background);
797
798 set_window_cursor(window->win,window->cursor_num);
799
800
801 if ((window->wm_hints = XAllocWMHints())!=NULL)
802 {
803 window->wm_hints->initial_state = NormalState;
804 window->wm_hints->input = True;
805 window->wm_hints->flags = StateHint | InputHint;
806 XSetWMHints(display,window->win, window->wm_hints);
807 };
808 if ((window->size_hints = XAllocSizeHints())!=NULL)
809 {
810 window->size_hints->flags = PPosition | PSize | PMinSize;
811 window->size_hints->min_width = 50;
812 window->size_hints->min_height = 50;
813 XSetWMNormalHints(display,window->win,window->size_hints);
814 };
815
816 XSelectInput(display, window->win, KeyPressMask | ButtonPressMask
817 | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask
818 | PointerMotionMask | ButtonMotionMask
819 | StructureNotifyMask
820 | ExposureMask | StructureNotifyMask | FocusChangeMask);
821
822 if (parent==RootWindow(display,screen))
823 XSetWMProtocols(display,window->win, &windelete, 1);
824
825 XMoveResizeWindow(display, window->win, window->x, window->y, window->w, window->h);
826
827 templ = add_to_list(*list,(ulong)window->win,(ulong)type,data);
828
829 if (templ==NULL)
830 return FALSE;
831
832 *list = templ;
833
834 return TRUE;
835 }
836
837 /* set the window title */
838 void
set_window_name(WindowStruct * window,char * name)839 set_window_name(WindowStruct *window,char *name)
840 {
841 XTextProperty textprop;
842
843 XStringListToTextProperty(&name,1,&textprop);
844
845 XSetWMName(display,window->win, &textprop);
846 }
847
848 /* constrain a line to 90 degrees */
849 inline void
constrain_line(long x1,long y1,long * x2,long * y2)850 constrain_line(long x1, long y1, long *x2, long *y2)
851 {
852 ((abs(*x2-x1))>(abs(*y2-y1))) ? (*y2 = y1) : (*x2 = x1);
853 }
854
855 /* constrain proportionally */
856 inline void
constrain_resize(double * xr,double * yr)857 constrain_resize(double *xr, double *yr)
858 {
859 (*xr>*yr) ? (*yr= *xr) : (*xr = *yr);
860 }
861
862 /* constrain proportionally */
863 inline void
constrain_ellipse(long * xr,long * yr)864 constrain_ellipse(long *xr, long *yr)
865 {
866 (*xr>*yr) ? (*yr= *xr) : (*xr = *yr);
867 }
868
869 /* constrain proportionally */
870 inline void
constrain_rectangle(long x1,long y1,long * x2,long * y2)871 constrain_rectangle(long x1, long y1, long *x2, long *y2)
872 {
873 ((abs(*x2-x1))>(abs(*y2-y1))) ? (*y2 = (*x2-x1+y1)) : (*x2 = (*y2-y1+x1));
874 }
875
876 void
deselect_everything()877 deselect_everything()
878 {
879 List l;
880 Boolean redraw=FALSE;
881 Object *ob=NULL;
882
883 l = state.views;
884
885 while (l!=NULL)
886 {
887 if (VIEW(l)->selected_object!=NULL)
888 {
889 redraw = TRUE;
890 ob = VIEW(l)->selected_object->ob;
891 VIEW(l)->selected_object = NULL;
892 };
893
894 if (VIEW(l)->highlighted_object!=NULL)
895 {
896 redraw = TRUE;
897 ob = VIEW(l)->highlighted_object->ob;
898 VIEW(l)->highlighted_object = NULL;
899 };
900
901 if (VIEW(l)->edited_object!=NULL)
902 {
903 redraw = TRUE;
904 ob = VIEW(l)->edited_object->ob;
905 };
906
907 if (redraw)
908 send_redraw_object(VIEW(l), ob);
909
910 VIEW(l)->edited_object = NULL;
911
912 redraw = FALSE;
913
914 l = l->next;
915 };
916
917 }
918
919 /* is a point x,y in an ellipse */
920 Boolean
point_in_ellipse(long cx,long cy,long a,long b,long x,long y)921 point_in_ellipse(long cx, long cy, long a, long b, long x, long y)
922 {
923 double xda = ((double)(x-cx))/((double)a);
924 double ydb = ((double)(y-cy))/((double)b);
925
926 return ((xda*xda)+(ydb*ydb) < 1.0);
927 }
928
929 /* does a box and an ellipse intersect */
930 int
ellipse_box(long cx,long cy,long a,long b,long x1,long y1,long x2,long y2)931 ellipse_box(long cx, long cy, long a, long b, long x1, long y1, long x2, long y2)
932 {
933 /* returns CONTAINS if ellipse contains box */
934 /* returns CONTAINED if box contains ellipse or intersects */
935
936 if (point_in_ellipse(cx,cy,a,b,x1,y1) && point_in_ellipse(cx,cy,a,b,x2,y1) &&
937 point_in_ellipse(cx,cy,a,b,x1,y2) && point_in_ellipse(cx,cy,a,b,x2,y2))
938 return CONTAINS;
939
940 return CONTAINED;
941 }
942
943 /* get a font of specified size and type */
944 /* nb point size is *scaled* point size */
945 VFont *
get_font(int fontnumber,int pointsize)946 get_font(int fontnumber, int pointsize)
947 {
948 char name[100];
949 char a[10];
950 int realsize;
951 VFont *vf;
952 List l;
953 char name2[100];
954 char **availfonts;
955 int bfontsize=0;
956 int numf=0;
957
958 if (pointsize < 20)
959 return NULL;
960 /* i.e. greek the text, no font */
961
962 if (pointsize >3000)
963 return NULL;
964
965 l = afonts;
966
967 while (l)
968 {
969 if (fontnumber==FONT(l)->num && FONT(l)->size==pointsize)
970 break;
971 l = l->next;
972 };
973
974 if (l)
975 return FONT(l);
976
977 /* this code is set up to get scaled bitmap fonts as per Nye A.3.2 */
978 /* they look terrible but that's X for you */
979
980 /* -adobe-courier-bold-o-normal-*-10-100-75-75-m-60-iso8859-1 */
981 /* -foundry-family-weight-slant-setwidth-"unknown"-pixels-10thpoints-hdpi-vdpi-spacing-avewidth-charset */
982 vf = (VFont *)malloc(sizeof(VFont));
983 vf->num = fontnumber;
984 vf->size = pointsize;
985 strcpy(name,fontnames[fontnumber][0]);
986 realsize = ((int)((pointsize/(POINTS_IN_INCH))*screen_ppi));
987 sprintf(a,"*-*-%d",realsize);
988 strcat(name,a);
989 /* REQUIRES the iso8859-1 to load */
990 strcat(name,"-75-75-*-*-iso8859-1");
991
992 /* assignment used as truth value :) */
993 if ((vf->x=XLoadQueryFont(display,name)))
994 {
995 afonts = add_to_list(afonts,0,0,(void *)vf);
996 return vf;
997 };
998
999 /* if we get here, font server won't scale bitmaps or font really isn't available */
1000 /* there are a surprising number of font servers out there not doing this :( */
1001
1002 /* search for the nearest */
1003
1004 strcpy(name2,fontnames[fontnumber][0]);
1005 strcat(name2,"*-*-*-*-*-*-*-iso8859-1");
1006 /* not ordered by size */
1007 availfonts = XListFonts(display, name2, 200, &numf);
1008 if (availfonts)
1009 {
1010 while (numf)
1011 {
1012 char *f = *(availfonts+numf-1);
1013 int asize;
1014 char *g;
1015
1016
1017 while (*f!='\0' && (*f!='-' || *(f+1)!='-'))
1018 f++;
1019
1020 if (*f=='\0')
1021 continue;
1022
1023 /* -- */
1024 f+=2;
1025 /* skip pixel size */
1026 while(*f!='\0' && *f!='-')
1027 f++;
1028
1029 if (*f=='\0')
1030 continue;
1031
1032 /* - */
1033 g = ++f;
1034
1035 while (*g!='\0' && *g!='-')
1036 g++;
1037
1038 *g='\0';
1039 /* pointsize */
1040 sscanf(f,"%d",&asize);
1041 /* pick biggest smaller than realsize */
1042 if (asize<=realsize && asize>bfontsize)
1043 bfontsize=asize;
1044
1045 numf--;
1046 };
1047
1048 if (bfontsize)
1049 {
1050 char name3[10];
1051 /* XListFonts returns stuff like "-adobe-times-medium-r-normal--12-120" */
1052 strcpy(name2,fontnames[fontnumber][0]);
1053 sprintf(name3,"*-*-%d",bfontsize);
1054 strcat(name2,name3);
1055 strcat(name2,"-*-*-*-*-iso8859-1");
1056 if ((vf->x=XLoadQueryFont(display,name2))!=NULL)
1057 {
1058 /* if the font is way out, warn user */
1059 if (abs(bfontsize-realsize)>100)
1060 fprintf(stderr,"figurine: warning: using font %s\nfigurine:instead of %s\n",name2,name);
1061
1062 XFreeFontNames(availfonts);
1063 afonts = add_to_list(afonts,0,0,(void *)vf);
1064 return vf;
1065 };
1066 };
1067 /* failure */
1068 XFreeFontNames(availfonts);
1069 };
1070
1071 fprintf(stderr, "figurine: Could not load font %s\n", name);
1072 free(vf);
1073
1074 /* careful not to infinitely recurse */
1075 if (fontnumber!=0 && fontnumber!=35)
1076 return get_font(0, pointsize); /* first try times replica */
1077
1078 return NULL; /* ok, have to greek */
1079 }
1080
1081 /* switch the icons on every view window to reflect current
1082 drawing mode / object */
1083
1084 #define off(a) stk_hide_icon((a),v->view_icon_window->win)
1085 #define onx(a) do { stk_show_icon((a),v->view_icon_window->win); \
1086 stk_set_x((a),v->view_icon_window->win, x); \
1087 x += VIEW_ICON_WIDTH; } while (0)
1088
1089
1090 void
switch_icons(View * us)1091 switch_icons(View *us)
1092 {
1093 List l;
1094 View *v;
1095 int a=state.current_icon;
1096 IconType temp;
1097 int x;
1098
1099 l = state.views;
1100
1101 while (l!=NULL)
1102 {
1103 v = VIEW(l);
1104 x=4*VIEW_ICON_WIDTH;
1105
1106 if (v!=us && us->selected_object && state.current_icon==POINTERICON)
1107 {
1108 l=l->next;
1109 continue;
1110 };
1111
1112 off(LINESTYLEICON);
1113 off(COLOURICON);
1114 off(FILLSTYLEICON);
1115 off(FILLCOLOURICON);
1116 off(LINEWIDTHICON);
1117 off(ENDSTYLEICON);
1118 off(JOINSTYLEICON);
1119 off(ARROWICON);
1120 off(SETPOLYGONICON);
1121 off(FONTICON);
1122 off(JUSTIFICATIONICON);
1123 off(SETSPLINEICON);
1124 off(ROUNDBOXICON);
1125 off(SETARCELLIPSEICON);
1126
1127 if (state.current_icon!=POINTERICON || !v->selected_object)
1128 a = state.current_icon;
1129
1130 if (state.current_icon==POINTERICON && v->selected_object!=NULL)
1131 {
1132 switch (v->selected_object->ob->type)
1133 {
1134 case ARCELLIPSE: a = ARCELLIPSEICON; break;
1135 case ELLIPSE: a = ELLIPSEICON; break;
1136 case POLYLINE: a = POLYLINEICON; break;
1137 case POLYGON: a = POLYGONICON; break;
1138 case SPLINE:
1139 case ARC:
1140 a = SPLINEICON;
1141 break;
1142
1143 case TEXT:
1144 if (v->selected_object->ob->ob.text.node)
1145 {
1146 onx(LINESTYLEICON);
1147 onx(COLOURICON);
1148 onx(FILLSTYLEICON);
1149 onx(FILLCOLOURICON);
1150 onx(LINEWIDTHICON);
1151 }
1152 else
1153 {
1154 a = 0;
1155 onx(COLOURICON);
1156 };
1157 break;
1158
1159
1160 case ROUNDBOX:
1161 onx(LINESTYLEICON);
1162 onx(COLOURICON);
1163 onx(FILLSTYLEICON);
1164 onx(FILLCOLOURICON);
1165 onx(LINEWIDTHICON);
1166 onx(ROUNDBOXICON);
1167 break;
1168
1169 default: a = state.current_icon; break;
1170 };
1171 };
1172
1173 switch(a)
1174 {
1175 case 0: break;
1176 case COMPOUNDICON: break;
1177
1178 case POLYLINEICON:
1179 onx(LINESTYLEICON); onx(COLOURICON);
1180 onx(LINEWIDTHICON); onx(ENDSTYLEICON);
1181 onx(JOINSTYLEICON); onx(ARROWICON);
1182 break;
1183
1184 case ARCELLIPSEICON:
1185 onx(LINESTYLEICON); onx(COLOURICON);
1186 onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1187 onx(LINEWIDTHICON); onx(ENDSTYLEICON);
1188 onx(SETARCELLIPSEICON); onx(ARROWICON);
1189 break;
1190
1191 case ELLIPSEICON:
1192 case BOXELLIPSEICON:
1193 onx(LINESTYLEICON); onx(COLOURICON);
1194 onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1195 onx(LINEWIDTHICON);
1196 break;
1197
1198 case RECTANGLEICON:
1199 onx(LINESTYLEICON); onx(COLOURICON);
1200 onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1201 onx(LINEWIDTHICON); onx(JOINSTYLEICON);
1202 onx(ROUNDBOXICON);
1203 break;
1204
1205 case POLYGONICON:
1206 onx(LINESTYLEICON); onx(COLOURICON);
1207 onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1208 onx(LINEWIDTHICON); onx(JOINSTYLEICON);
1209 if (state.current_icon!=POINTERICON)
1210 onx(SETPOLYGONICON);
1211 break;
1212
1213 case SPLINEICON:
1214 /* yuk */
1215 onx(LINESTYLEICON); onx(COLOURICON);
1216 if (state.current_icon!=POINTERICON || v->selected_object)
1217 {
1218 onx(FILLSTYLEICON);
1219 onx(FILLCOLOURICON);
1220 };
1221 onx(LINEWIDTHICON);
1222 if (state.current_icon!=POINTERICON || v->selected_object)
1223 {
1224 onx(ENDSTYLEICON);
1225 onx(ARROWICON);
1226 };
1227 if (state.current_icon!=POINTERICON)
1228 onx(SETSPLINEICON);
1229 break;
1230
1231 case TEXTICON:
1232 onx(COLOURICON); onx(FONTICON);
1233 onx(JUSTIFICATIONICON);
1234 break;
1235
1236 case NODEICON:
1237 onx(LINESTYLEICON); onx(COLOURICON);
1238 onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1239 onx(LINEWIDTHICON); onx(FONTICON);
1240 onx(JUSTIFICATIONICON);
1241 break;
1242
1243 case ARCICON:
1244 onx(LINESTYLEICON); onx(COLOURICON);
1245 onx(LINEWIDTHICON); onx(ENDSTYLEICON);
1246 onx(ARROWICON);
1247 onx(FONTICON); onx(JUSTIFICATIONICON);
1248 break;
1249 };
1250
1251 if (v->selected_object!=NULL)
1252 {
1253 Object *ob = v->selected_object->ob;
1254
1255 /* linestyle */
1256 if (ob->type!=TEXT && ob->type!=COMPOUND)
1257 {
1258 switch((signed)ob->ls)
1259 {
1260 case SOLID: temp=SOLIDICON; break;
1261 case DASHED: temp=DASHEDICON; break;
1262 case DOTTED: temp=DOTTEDICON; break;
1263 case DASH_DOTTED: temp=DASHDOTTEDICON; break;
1264 case DASH_DOUBLE_DOTTED: temp=DASHDOUBLEDOTTEDICON; break;
1265 case DASH_TRIPLE_DOTTED: temp=DASHTRIPLEDOTTEDICON; break;
1266 case -1: temp=SOLIDICON; break;
1267 default: temp=0; break;
1268 };
1269 stk_swap_pixmap(LINESTYLEICON, v->view_icon_window->win,(signed)temp,linestyle_menu);
1270 };
1271
1272 /* endstyle */
1273 if (ob->type==POLYLINE || ((ob->type==SPLINE || ob->type==ARC) && !ob->ob.spline.closed))
1274 {
1275 switch(ob->es)
1276 {
1277 case CapRound: temp=ROUNDENDICON; break;
1278 case CapButt: temp=FLATENDICON; break;
1279 case CapProjecting: temp=PROJECTINGENDICON; break;
1280 default: temp=0; break;
1281 };
1282 stk_swap_pixmap(ENDSTYLEICON, v->view_icon_window->win,(signed)temp,endstyle_menu);
1283 };
1284
1285 /* joinstyle */
1286 if (ob->type==POLYGON || ob->type==POLYLINE)
1287 {
1288 switch(ob->js)
1289 {
1290 case JoinRound: temp=ROUNDJOINICON; break;
1291 case JoinMiter: temp=SHARPJOINICON; break;
1292 case JoinBevel: temp=BEVELJOINICON; break;
1293 default: temp=0; break;
1294 };
1295 stk_swap_pixmap(JOINSTYLEICON, v->view_icon_window->win,(signed)temp,joinstyle_menu);
1296 };
1297
1298 /* colour */
1299 if (ob->type==ARCELLIPSE || ob->type==POLYGON ||
1300 ob->type==ROUNDBOX || ob->type==ELLIPSE ||
1301 ob->type==POLYLINE || ob->type==TEXT ||
1302 ob->type==SPLINE || ob->type==ARC)
1303 {
1304 if (ob->colour<332)
1305 {
1306 stk_swap_pixmap(COLOURICON, v->view_icon_window->win,(signed)ob->colour,colour_menu);
1307 v->colour = ob->colour;
1308 };
1309 };
1310
1311 /* fill colour */
1312 if (ob->type==ARCELLIPSE || ob->type==POLYGON ||
1313 ob->type==ROUNDBOX || ob->type==ELLIPSE ||
1314 ob->type==SPLINE || ob->type==ARC)
1315 {
1316 if (ob->fillcolour<432)
1317 {
1318 stk_swap_pixmap(FILLCOLOURICON, v->view_icon_window->win,(signed)ob->fillcolour,fillcolour_menu);
1319 v->fillcolour = ob->fillcolour;
1320 };
1321 };
1322
1323 /* fill style */
1324 if (ob->type==ARCELLIPSE || ob->type==POLYGON ||
1325 ob->type==ROUNDBOX || ob->type==ELLIPSE ||
1326 ob->type==SPLINE || ob->type==ARC)
1327 {
1328 if (ob->fs==-1)
1329 stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win, 243, fillstyle_menu);
1330 else
1331 stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win, STARTOFFILLSTYLES+ob->fs, fillstyle_menu);
1332 v->fillstyle = ob->fs;
1333 };
1334
1335 /* node special cases */
1336 if (ob->type==TEXT && ob->ob.text.node)
1337 {
1338 if (ob->fillcolour<432)
1339 {
1340 stk_swap_pixmap(FILLCOLOURICON, v->view_icon_window->win,
1341 (signed)ob->ob.text.ellipse->fillcolour, fillcolour_menu);
1342 v->fillcolour = ob->ob.text.ellipse->fillcolour;
1343 };
1344
1345 if (ob->ob.text.ellipse->fs==-1)
1346 stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win, 243, fillstyle_menu);
1347 else
1348 stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win,
1349 STARTOFFILLSTYLES+ob->ob.text.ellipse->fs, fillstyle_menu);
1350 v->fillstyle = ob->ob.text.ellipse->fs;
1351 };
1352
1353 /* arcellipse type */
1354 if (ob->type==ARCELLIPSE)
1355 {
1356 if (ob->ob.arcellipse.open)
1357 stk_swap_pixmap(SETARCELLIPSEICON, v->view_icon_window->win, OPENARCELLIPSEICON, setarcellipse_menu);
1358 else
1359 stk_swap_pixmap(SETARCELLIPSEICON, v->view_icon_window->win, CLOSEDARCELLIPSEICON, setarcellipse_menu);
1360 v->openarc = ob->ob.arcellipse.open;
1361 };
1362 };
1363 l = l->next;
1364 };
1365
1366 }
1367
1368 /* calc a mid point bent by angle */
1369 void
bent_midpoint(long x1,long y1,long x2,long y2,double angle,short * ox,short * oy)1370 bent_midpoint(long x1,long y1,long x2,long y2,double angle, short *ox, short *oy)
1371 {
1372 long x3,y3;
1373 double sina,cosa;
1374
1375 sina = sin(angle);
1376 cosa = cos(angle);
1377
1378 x3 = (x2-x1)/2;
1379 y3 = (y2-y1)/2;
1380
1381 *ox = x1 + cosa*x3 - sina*y3;
1382 *oy = y1 + sina*x3 + cosa*y3;
1383 }
1384
1385 /* snap a value by grid size */
1386 long
snap(long size,long val)1387 snap(long size, long val)
1388 {
1389 if ((val % size) > (size/2))
1390 return ((val/size)+1)*size;
1391 else
1392 return (val/size)*size;
1393 }
1394