1 /*
2 * FIG : Facility for Interactive Generation of figures
3 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4 * Parts Copyright (c) 1989-2015 by Brian V. Smith
5 * Parts Copyright (c) 1991 by Paul King
6 * Parts Copyright (c) 2016-2020 by Thomas Loimer
7 *
8 * Parts Copyright (c) 1992 by James Tough
9 * Parts Copyright (c) 1998 by Georg Stemmer
10 * Parts Copyright (c) 1995 by C. Blanc and C. Schlick
11 *
12 * Any party obtaining a copy of these files is granted, free of charge, a
13 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
14 * nonexclusive right and license to deal in this software and documentation
15 * files (the "Software"), including without limitation the rights to use,
16 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
17 * the Software, and to permit persons who receive copies from any such
18 * party to do so, with the only requirement being that the above copyright
19 * and this permission notice remain intact.
20 *
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <X11/Intrinsic.h> /* includes X11/Xlib.h */ /* Boolean */
27 #include <X11/ImUtil.h> /* must first include X11/Xlib.h */
28 /* _XInitImageFuncPtrs() */
29
30 #include "resources.h"
31 #include "mode.h"
32 #include "object.h"
33 #include "d_text.h" /* reload_text_fstruct() */
34 #include "f_util.h" /* xf_basename() */
35 #include "u_bound.h" /* <obj>_bound(), overlapping() */
36 #include "u_draw.h"
37 #include "u_geom.h" /* compute_angle() */
38 #include "u_error.h" /* X_error_handler() */
39 #include "w_canvas.h" /* clip_xmax, clip_xmin */
40 #include "w_file.h" /* check_cancel() */
41 #include "w_layers.h" /* active_layer() */
42 #include "w_msgpanel.h" /* put_msg() */
43 #include "w_util.h" /* NUM_ARROW_TYPES */
44 #include "u_redraw.h" /* redisplay_line() */
45 #include "w_cursor.h" /* reset_cursor() */
46 #include "xfig_math.h"
47
48 static Boolean add_point(int x, int y);
49 static void init_point_array(void);
50 static Boolean add_closepoint(void);
51
52 /* the spline definition stuff has been moved to u_draw_spline.c */
53 #include "u_draw_spline.c"
54
55 /************** ARRAY FOR ARROW SHAPES **************/
56
57 struct _fpnt {
58 double x,y;
59 };
60
61 struct _arrow_shape {
62 int numpts; /* number of points in arrowhead */
63 int tipno; /* which point contains the tip */
64 int numfillpts; /* number of points to fill */
65 Boolean simplefill; /* if true, use points array to fill otherwise use fill_points array */
66 Boolean clip; /* if false, no clip area needed (e.g. for reverse triangle arrowhead) */
67 Boolean half; /* if true, arrowhead is half-wide and must be shifted to cover the line */
68 double tipmv; /* acuteness of tip (smaller angle, larger tipmv) */
69 struct _fpnt points[6]; /* points in arrowhead */
70 struct _fpnt fillpoints[6]; /* points to fill if not "simple" */
71 };
72
73 static struct _arrow_shape arrow_shapes[NUM_ARROW_TYPES] = {
74 /* number of points, index of tip, {datapairs} */
75 /* first point must be upper-left point of tail, then tip */
76
77 /* type 0 */
78 { 3, 1, 0, True, True, False, 2.15, {{-1,0.5}, {0,0}, {-1,-0.5}}, {0}},
79 /* place holder for what would be type 0 filled */
80 { 0 },
81 /* type 1a simple triangle */
82 { 4, 1, 0, True, True, False, 2.1,
83 {{-1.0,0.5}, {0.,0.}, {-1.0,-0.5}, {-1.0,0.5}}, {0}},
84 /* type 1b filled simple triangle*/
85 { 4, 1, 0, True, True, False, 2.1,
86 {{-1.0,0.5}, {0.,0.}, {-1.0,-0.5}, {-1.0,0.5}}, {0}},
87 /* type 2a concave spearhead */
88 { 5, 1, 0, True, True, False, 2.6,
89 {{-1.25,0.5},{0.,0.},{-1.25,-0.5},{-1.,0.},{-1.25,0.5}},
90 {0}},
91 /* type 2b filled concave spearhead */
92 { 5, 1, 0, True, True, False, 2.6,
93 {{-1.25,0.5},{0.,0.},{-1.25,-0.5},{-1.,0.},{-1.25,0.5}},
94 {0}},
95 /* type 3a convex spearhead */
96 { 5, 1, 0, True, True, False, 1.5,
97 {{-0.75,0.5},{0.,0.},{-0.75,-0.5},{-1.,0.},{-0.75,0.5}},
98 {0}},
99 /* type 3b filled convex spearhead */
100 { 5, 1, 0, True, True, False, 1.5,
101 {{-0.75,0.5},{0.,0.},{-0.75,-0.5},{-1.,0.},{-0.75,0.5}},
102 {0}},
103 /* type 4a diamond */
104 { 5, 1, 0, True, True, False, 1.15,
105 {{-0.5,0.5},{0.,0.},{-0.5,-0.5},{-1.0,0.},{-0.5,0.5}},
106 {0}},
107 /* type 4b filled diamond */
108 { 5, 1, 0, True, True, False, 1.15,
109 {{-0.5,0.5},{0.,0.},{-0.5,-0.5},{-1.0,0.},{-0.5,0.5}},
110 {0}},
111 /* type 5a/b circle - handled in code */
112 { 0, 0, 0, True, True, False, 0.0, {0}, {0}},
113 { 0, 0, 0, True, True, False, 0.0, {0}, {0}},
114 /* type 6a/b half circle - handled in code */
115 { 0, 0, 0, True, True, False, -1.0, {0}, {0}},
116 { 0, 0, 0, True, True, False, -1.0, {0}, {0}},
117 /* type 7a square */
118 { 5, 1, 0, True, True, False, 0.0,
119 {{-1.0,0.5},{0.,0.5},{0.,-0.5},{-1.0,-0.5},{-1.0,0.5}},
120 {0}},
121 /* type 7b filled square */
122 { 5, 1, 0, True, True, False, 0.0,
123 {{-1.0,0.5},{0.,0.5},{0.,-0.5},{-1.0,-0.5},{-1.0,0.5}},
124 {0}},
125 /* type 8a reverse triangle */
126 { 4, 1, 0, True, False, False, 0.0,
127 {{-1.0,0.},{0.,0.5},{0.,-0.5},{-1.0,0}}, {0}},
128 /* type 8b filled reverse triangle */
129 { 4, 1, 0, True, False, False, 0.0,
130 {{-1.0,0.},{0.,0.5},{0.,-0.5},{-1.0,0.}}, {0}},
131 /* type 9a top-half filled concave spearhead */
132 { 5, 1, 3, False, True, False, 2.6,
133 {{-1.25,0.5},{0.,0.},{-1.25,-0.5},{-1.,0.},{-1.25,0.5}},
134 {{-1.25,-0.5},{0.,0.},{-1.,0.}}},
135 /* type 9b bottom-half filled concave spearhead */
136 { 5, 1, 3, False, True, False, 2.6,
137 {{-1.25,0.5},{0.,0.},{-1.25,-0.5},{-1.,0.},{-1.25,0.5}},
138 {{-1.25,0.5},{0.,0.},{-1,0}}},
139 /* type 10o top-half simple triangle */
140 { 4, 1, 0, True, True, True, 2.5,
141 {{-1.0,0.5}, {0.,0.}, {-1.,0.0}, {-1.0,0.5}}, {0}},
142 /* type 10f top-half filled simple triangle*/
143 { 4, 1, 0, True, True, True, 2.5,
144 {{-1.0,0.5}, {0.,0.}, {-1.,0.0}, {-1.0,0.5}}, {0}},
145 /* type 11o top-half concave spearhead */
146 { 4, 1, 0, True, True, True, 3.5,
147 {{-1.25,0.5}, {0.,0.}, {-1.,0.}, {-1.25,0.5}}, {0}},
148 /* type 11f top-half filled concave spearhead */
149 { 4, 1, 0, True, True, True, 3.5,
150 {{-1.25,0.5}, {0.,0.}, {-1.,0.}, {-1.25,0.5}}, {0}},
151 /* type 12o top-half convex spearhead */
152 { 4, 1, 0, True, True, True, 2.5,
153 {{-0.75,0.5}, {0.,0.}, {-1.,0.}, {-0.75,0.5}}, {0}},
154 /* type 12f top-half filled convex spearhead */
155 { 4, 1, 0, True, True, True, 2.5,
156 {{-0.75,0.5}, {0.,0.}, {-1.,0.}, {-0.75,0.5}}, {0}},
157 /* type 13a "wye" */
158 { 3, 0, 0, True, True, False, -1.0,
159 {{0,0.5},{-1.0,0.},{0.,-0.5}}, {0}},
160 /* type 13b bar */
161 { 2, 1, 0, True, True, False, 0.0, {{0.,0.5},{0.,-0.5}}, {0}},
162 /* type 14a two-prong fork */
163 { 4, 0, 0, True, True, False, -1.0,
164 {{0,0.5},{-1.0,0.5},{-1.0,-0.5},{0.,-0.5}}, {0}},
165 /* type 14b backward two-prong fork */
166 { 4, 1, 0, True, True, False, 0.0,
167 {{-1.0,0.5,},{0.,0.5},{0.,-0.5},{-1.0,-0.5}}, {0}},
168 };
169
170 /************** POLYGON/CURVE DRAWING FACILITIES ****************/
171
172 static int npoints;
173 static zXPoint *points = NULL;
174 static int max_points = 0;
175 static int allocstep = 200;
176 static char bufx[10]; /* for appres.shownums */
177
178 /* these are for the arrowheads */
179 static zXPoint farpts[50],barpts[50];
180 static zXPoint farfillpts[50], barfillpts[50];
181 static int nfpts, nbpts, nffillpts, nbfillpts;
182
183 /************* Code begins here *************/
184
185
186 void clip_arrows (F_line *obj, int objtype, int op, int skip);
187 void draw_arrow (F_line *obj, F_arrow *arrow, zXPoint *points, int npoints, zXPoint *points2, int npoints2, int op);
188 void debug_depth (int depth, int x, int y);
189 void newpoint (float xp, float yp);
190 void draw_arcbox (F_line *line, int op);
191 void draw_pic_pixmap (F_line *box, int op);
192 void create_pic_pixmap (F_line *box, int rotation, int width, int height, int flipped);
193 void clr_mask_bit (int r, int c, int bwidth, unsigned char *mask);
194 void greek_text (F_text *text, int x1, int y1, int x2, int y2);
195
196 static void
init_point_array(void)197 init_point_array(void)
198 {
199 npoints = 0;
200 }
201
202 static Boolean
add_point(int x,int y)203 add_point(int x, int y)
204 {
205 if (npoints >= max_points) {
206 int tmp_n;
207 zXPoint *tmp_p;
208 tmp_n = max_points + allocstep;
209 /* too many points, return false */
210 if (tmp_n > MAXNUMPTS) {
211 if (appres.DEBUG)
212 fprintf(stderr,"add_point - reached MAXNUMPTS (%d)\n",tmp_n);
213 return False;
214 }
215 if (max_points == 0) {
216 tmp_p = (zXPoint *) malloc(tmp_n * sizeof(zXPoint));
217 if (appres.DEBUG)
218 fprintf(stderr,"add_point - alloc %d points\n",tmp_n);
219 } else {
220 tmp_p = (zXPoint *) realloc(points, tmp_n * sizeof(zXPoint));
221 if (appres.DEBUG)
222 fprintf(stderr,"add_point - realloc %d points\n",tmp_n);
223 }
224 if (tmp_p == NULL) {
225 fprintf(stderr,
226 "xfig: insufficient memory to allocate point array\n");
227 return False;
228 }
229 points = tmp_p;
230 max_points = tmp_n;
231 }
232 /* ignore identical points */
233 if (npoints > 0 && points[npoints-1].x == x && points[npoints-1].y ==
234 y)
235 return True;
236 points[npoints].x = x;
237 points[npoints].y = y;
238 npoints++;
239 return True;
240 }
241
draw_point_array(Window w,int op,int depth,int line_width,int line_style,float style_val,int join_style,int cap_style,int fill_style,int pen_color,int fill_color)242 void draw_point_array(Window w, int op, int depth, int line_width, int line_style, float style_val, int join_style, int cap_style, int fill_style, int pen_color, int fill_color)
243 {
244 pw_lines(w, points, npoints, op, depth, line_width, line_style, style_val,
245 join_style, cap_style, fill_style, pen_color, fill_color);
246 }
247
248 /*********************** ARC ***************************/
249
draw_arc(F_arc * a,int op)250 void draw_arc(F_arc *a, int op)
251 {
252 double rx, ry, rcx, rcy;
253 int cx, cy, scx, scy;
254 int radius;
255 int xmin, ymin, xmax, ymax;
256 int i;
257
258 arc_bound(a, &xmin, &ymin, &xmax, &ymax);
259 if (!overlapping(ZOOMX(xmin), ZOOMY(ymin), ZOOMX(xmax), ZOOMY(ymax),
260 clip_xmin, clip_ymin, clip_xmax, clip_ymax))
261 return;
262
263 rx = a->point[0].x - a->center.x;
264 ry = a->center.y - a->point[0].y;
265 radius = round(sqrt(rx * rx + ry * ry));
266
267 /* need both int and double center points */
268 cx = rcx = a->center.x;
269 cy = rcy = a->center.y;
270
271 /* show point numbers if requested */
272 if (appres.shownums && active_layer(a->depth)) {
273 /* we may have to enlarge the clipping area to include the center point of the arc */
274 scx = ZOOMX(cx);
275 scy = ZOOMY(cy);
276 if (scx < clip_xmin+10 || scx > clip_xmax-10 ||
277 scy < clip_ymin+10 || scy > clip_ymax-10)
278 set_clip_window(min2(scx-10,clip_xmin), min2(scy-10,clip_ymin),
279 max2(scx+10,clip_xmax), max2(scy+10,clip_ymax));
280 sprintf(bufx,"c");
281 pw_text(canvas_win, cx, round(cy-3.0/zoomscale),
282 op, a->depth, roman_font, 0.0, bufx, RED, COLOR_NONE);
283 pw_point(canvas_win, cx, cy, op, a->depth, 4, RED, CAP_ROUND);
284 for (i=1; i<=3; i++) {
285 /* label the point number above the point */
286 sprintf(bufx,"%d",i);
287 pw_text(canvas_win, a->point[i-1].x, round(a->point[i-1].y-3.0/zoomscale),
288 op, a->depth, roman_font, 0.0, bufx, RED, COLOR_NONE);
289 pw_point(canvas_win, a->point[i-1].x, a->point[i-1].y, op, a->depth, 4, RED, CAP_ROUND);
290 }
291 /* restore original clip window */
292 set_clip_window(clip_xmin, clip_ymin, clip_xmax, clip_ymax);
293 }
294 /* fill points array but don't display the points yet */
295
296 curve(canvas_win, a->depth,
297 round(a->point[0].x - rcx),
298 round(rcy - a->point[0].y),
299 round(a->point[2].x - rcx),
300 round(rcy - a->point[2].y),
301 DONT_DRAW_POINTS, (a->type == T_PIE_WEDGE_ARC? DRAW_CENTER: DONT_DRAW_CENTER),
302 a->direction, radius, radius,
303 round(rcx), round(rcy), op,
304 a->thickness, a->style, a->style_val, a->fill_style,
305 a->pen_color, a->fill_color, a->cap_style);
306
307 /* setup clipping so that spline doesn't protrude beyond arrowhead */
308 /* also create the arrowheads */
309 clip_arrows((F_line *)a,O_ARC,op,0);
310
311 /* draw the arc itself */
312 draw_point_array(canvas_win, op, a->depth, a->thickness,
313 a->style, a->style_val, JOIN_BEVEL,
314 a->cap_style, a->fill_style,
315 a->pen_color, a->fill_color);
316
317 /* restore clipping */
318 set_clip_window(clip_xmin, clip_ymin, clip_xmax, clip_ymax);
319
320 /* draw the arrowheads, if any */
321 if (a->type != T_PIE_WEDGE_ARC) {
322 if (a->for_arrow) {
323 draw_arrow((F_line *)a, a->for_arrow, farpts, nfpts, farfillpts, nffillpts, op);
324 }
325 if (a->back_arrow) {
326 draw_arrow((F_line *)a, a->back_arrow, barpts, nbpts, barfillpts, nbfillpts, op);
327 }
328 }
329 /* write the depth on the object */
330 debug_depth(a->depth,a->point[0].x,a->point[0].y);
331 }
332
333 /*********************** ELLIPSE ***************************/
334
draw_ellipse(F_ellipse * e,int op)335 void draw_ellipse(F_ellipse *e, int op)
336 {
337 int a, b, xmin, ymin, xmax, ymax;
338
339 ellipse_bound(e, &xmin, &ymin, &xmax, &ymax);
340 if (!overlapping(ZOOMX(xmin), ZOOMY(ymin), ZOOMX(xmax), ZOOMY(ymax),
341 clip_xmin, clip_ymin, clip_xmax, clip_ymax))
342 return;
343
344 if (e->angle != 0.0) {
345 angle_ellipse(e->center.x, e->center.y, e->radiuses.x, e->radiuses.y,
346 e->angle, op, e->depth, e->thickness, e->style,
347 e->style_val, e->fill_style, e->pen_color, e->fill_color);
348 /* it is much faster to use curve() for dashed and dotted lines that to
349 use the server's sloooow algorithms for that */
350 } else if (op != ERASE && (e->style == DOTTED_LINE || e->style == DASH_LINE)) {
351 a = e->radiuses.x;
352 b = e->radiuses.y;
353 curve(canvas_win, e->depth, a, 0, a, 0, DRAW_POINTS, DONT_DRAW_CENTER, e->direction,
354 (b * b), (a * a),
355 e->center.x, e->center.y, op,
356 e->thickness, e->style, e->style_val, e->fill_style,
357 e->pen_color, e->fill_color, CAP_ROUND);
358 /* however, for solid lines the server is muuuch faster even for thick lines */
359 } else {
360 xmin = e->center.x - e->radiuses.x;
361 ymin = e->center.y - e->radiuses.y;
362 xmax = e->center.x + e->radiuses.x;
363 ymax = e->center.y + e->radiuses.y;
364 pw_curve(canvas_win, xmin, ymin, xmax, ymax, op, e->depth,
365 e->thickness, e->style, e->style_val, e->fill_style,
366 e->pen_color, e->fill_color, CAP_ROUND);
367 }
368 /* write the depth on the object */
369 debug_depth(e->depth,e->center.x,e->center.y);
370 }
371
372 static Boolean
add_closepoint(void)373 add_closepoint(void)
374 {
375 return add_point(points[0].x,points[0].y);
376 }
377
378 /*
379 * An Ellipse Generator.
380 * Written by James Tough 7th May 92
381 *
382 * The following routines displays a filled ellipse on the screen from the
383 * semi-minor axis 'a', semi-major axis 'b' and angle of rotation
384 * 'phi'.
385 *
386 * It works along these principles .....
387 *
388 * The standard ellipse equation is
389 *
390 * x*x y*y
391 * --- + ---
392 * a*a b*b
393 *
394 *
395 * Rotation of a point (x,y) is well known through the use of
396 *
397 * x' = x*COS(phi) - y*SIN(phi)
398 * y' = y*COS(phi) + y*COS(phi)
399 *
400 * Taking these to together, this gives the equation for a rotated
401 * ellipse centered around the origin.
402 *
403 * [x*COS(phi) - y*SIN(phi)]^2 [x*SIN(phi) + y*COS(phi)]^2
404 * --------------------------- + ---------------------------
405 * a*a b*b
406 *
407 * NOTE - some of the above equation can be precomputed, eg,
408 *
409 * i = COS(phi)/a and j = SIN(phi)/b
410 *
411 * NOTE - y is constant for each line so,
412 *
413 * m = -yk*SIN(phi)/a and n = yk*COS(phi)/b
414 * where yk stands for the kth line ( y subscript k)
415 *
416 * Where yk=y, we get a quadratic,
417 *
418 * (i*x + m)^2 + (j*x + n)^2 = 1
419 *
420 * Thus for any particular line, y, there is two corresponding x
421 * values. These are the roots of the quadratic. To get the roots,
422 * the above equation can be rearranged using the standard method,
423 *
424 * -(i*m + j*n) +- sqrt[i^2 + j^2 - (i*n -j*m)^2]
425 * x = ----------------------------------------------
426 * i^2 + j^2
427 *
428 * NOTE - again much of this equation can be precomputed.
429 *
430 * c1 = i^2 + j^2
431 * c2 = [COS(phi)*SIN(phi)*(a-b)]
432 * c3 = [b*b*(COS(phi)^2) + a*a*(SIN(phi)^2)]
433 * c4 = a*b/c3
434 *
435 * x = c2*y +- c4*sqrt(c3 - y*y), where +- must be evaluated once
436 * for plus, and once for minus.
437 *
438 * We also need to know how large the ellipse is. This condition
439 * arises when the sqrt of the above equation evaluates to zero.
440 * Thus the height of the ellipse is give by
441 *
442 * sqrt[ b*b*(COS(phi)^2) + a*a*(SIN(phi)^2) ]
443 *
444 * which just happens to be equal to sqrt(c3).
445 *
446 * It is now possible to create a routine that will scan convert
447 * the ellipse on the screen.
448 *
449 * NOTE - c2 is the gradient of the new ellipse axis.
450 * c4 is the new semi-minor axis, 'a'.
451 * sqr(c3) is the new semi-major axis, 'b'.
452 *
453 * These values could be used in a 4WS or 8WS ellipse generator
454 * that does not work on rotation, to give the feel of a rotated
455 * ellipse. These ellipses are not very accurate and give visable
456 * bumps along the edge of the ellipse. However, these routines
457 * are very quick, and give a good approximation to a rotated ellipse.
458 *
459 * NOTES on the code given.
460 *
461 * All the routines take there parameters as ( x, y, a, b, phi ),
462 * where x,y are the center of the ellipse ( relative to the
463 * origin ), a and b are the vertical and horizontal axis, and
464 * phi is the angle of rotation in RADIANS.
465 *
466 * The 'moveto(x,y)' command moves the screen cursor to the
467 * (x,y) point.
468 * The 'lineto(x,y)' command draws a line from the cursor to
469 * the point (x,y).
470 *
471 */
472
473
474 /*
475 * QuickEllipse, uses the same method as Ellipse, but uses incremental
476 * methods to reduce the amount of work that has to be done inside
477 * the main loop. The speed increase is very noticeable.
478 *
479 * Written by James Tough
480 * 7th May 1992
481 *
482 */
483
484 static int x[MAXNUMPTS/4][4],y[MAXNUMPTS/4][4];
485 static int nump[4];
486 static int totpts,i,j;
487 static int order[4]={0,1,3,2};
488
angle_ellipse(int center_x,int center_y,int radius_x,int radius_y,float angle,int op,int depth,int thickness,int style,float style_val,int fill_style,int pen_color,int fill_color)489 void angle_ellipse(int center_x, int center_y, int radius_x, int radius_y, float angle, int op, int depth, int thickness, int style, float style_val, int fill_style, int pen_color, int fill_color)
490 {
491 float xcen, ycen, a, b;
492
493 double c1, c2, c3, c4, c5, c6, v1, cphi, sphi, cphisqr, sphisqr;
494 double xleft, xright, d, asqr, bsqr;
495 int ymax, yy=0;
496 int k,m,dir;
497 float savezoom;
498 int savexoff, saveyoff;
499
500 if (radius_x == 0 || radius_y == 0)
501 return;
502
503 /* adjust for zoomscale so we iterate over zoomed pixels */
504 xcen = ZOOMX(center_x);
505 ycen = ZOOMY(center_y);
506 a = radius_x*zoomscale;
507 b = radius_y*zoomscale;
508 savezoom = zoomscale;
509 savexoff = zoomxoff;
510 saveyoff = zoomyoff;
511 zoomscale = 1.0;
512 zoomxoff = zoomyoff = 0;
513
514 cphi = cos((double)angle);
515 sphi = sin((double)angle);
516 cphisqr = cphi*cphi;
517 sphisqr = sphi*sphi;
518 asqr = a*a;
519 bsqr = b*b;
520
521 c1 = (cphisqr/asqr)+(sphisqr/bsqr);
522 c2 = ((cphi*sphi/asqr)-(cphi*sphi/bsqr))/c1;
523 c3 = (bsqr*cphisqr) + (asqr*sphisqr);
524 ymax = sqrt(c3);
525 c4 = a*b/c3;
526 c5 = 0;
527 v1 = c4*c4;
528 c6 = 2*v1;
529 c3 = c3*v1-v1;
530 totpts = 0;
531 for (i=0; i<=3; i++)
532 nump[i]=0;
533 i=0; j=0;
534 /* odd first points */
535 if (ymax % 2) {
536 d = sqrt(c3);
537 newpoint(xcen-d,ycen);
538 newpoint(xcen+d,ycen);
539 c5 = c2;
540 yy=1;
541 }
542 while (c3>=0) {
543 d = sqrt(c3);
544 xleft = c5-d;
545 xright = c5+d;
546 newpoint(xcen+xleft,ycen+yy);
547 newpoint(xcen+xright,ycen+yy);
548 newpoint(xcen-xright,ycen-yy);
549 newpoint(xcen-xleft,ycen-yy);
550 c5+=c2;
551 v1+=c6;
552 c3-=v1;
553 yy=yy+1;
554 }
555 dir=0;
556 totpts++; /* add another point to join with first */
557 init_point_array();
558 /* now go down the 1st column, up the 2nd, down the 4th
559 and up the 3rd to get the points in the correct order */
560 for (k=0; k<=3; k++) {
561 if (dir==0)
562 for (m=0; m<nump[k]; m++) {
563 if (!add_point(x[m][order[k]],y[m][order[k]]))
564 break;
565 }
566 else
567 for (m=nump[k]-1; m>=0; m--) {
568 if (!add_point(x[m][order[k]],y[m][order[k]]))
569 break;
570 }
571 dir = 1-dir;
572 } /* next k */
573 /* add another point to join with first */
574 if (!add_point(points[0].x,points[0].y))
575 too_many_points();
576 draw_point_array(canvas_win, op, depth, thickness, style, style_val,
577 JOIN_BEVEL, CAP_ROUND, fill_style, pen_color, fill_color);
578
579 zoomscale = savezoom;
580 zoomxoff = savexoff;
581 zoomyoff = saveyoff;
582 return;
583 }
584
585 /* store the points across (row-wise in) the matrix */
586
newpoint(float xp,float yp)587 void newpoint(float xp, float yp)
588 {
589 if (totpts >= MAXNUMPTS/4) {
590 if (totpts == MAXNUMPTS/4) {
591 put_msg("Too many points to fully display rotated ellipse. %d points max",
592 MAXNUMPTS);
593 totpts++;
594 }
595 return;
596 }
597 x[i][j]=round(xp);
598 y[i][j]=round(yp);
599 nump[j]++;
600 totpts++;
601 if (++j > 3) {
602 j=0;
603 i++;
604 }
605 }
606
607
608 /*********************** LINE ***************************/
609
draw_line(F_line * line,int op)610 void draw_line(F_line *line, int op)
611 {
612 F_point *point;
613 int i, x, y;
614 int xmin, ymin, xmax, ymax;
615 char *string;
616 F_point *p0, *p1, *p2;
617 PR_SIZE txt;
618
619 line_bound(line, &xmin, &ymin, &xmax, &ymax);
620 if (!overlapping(ZOOMX(xmin), ZOOMY(ymin), ZOOMX(xmax), ZOOMY(ymax),
621 clip_xmin, clip_ymin, clip_xmax, clip_ymax))
622 return;
623
624 /* is it an arcbox? */
625 if (line->type == T_ARCBOX) {
626 draw_arcbox(line, op);
627 return;
628 }
629 /* is it a picture object or a Fig figure? */
630 if (line->type == T_PICTURE) {
631 if (line->pic->pic_cache) {
632 if ((line->pic->pic_cache->bitmap != NULL) && active_layer(line->depth)) {
633 /* only draw the picture if there is a pixmap AND this layer is active */
634 draw_pic_pixmap(line, op);
635 return;
636 } else if (line->pic->pic_cache->bitmap != NULL) {
637 /* if there is a pixmap but the layer is not active, draw it as a filled box */
638 line->type = T_BOX;
639 line->fill_style = NUMSHADEPATS-1; /* fill it */
640 draw_line(line, op);
641 line->type = T_PICTURE;
642 return;
643 }
644 }
645
646 /* either there is no pixmap or this layer is grayed out,
647 label empty pic bounding box */
648 if (!line->pic->pic_cache || line->pic->pic_cache->file[0] == '\0')
649 string = EMPTY_PIC;
650 else {
651 string = xf_basename(line->pic->pic_cache->file);
652 }
653 p0 = line->points;
654 p1 = p0->next;
655 p2 = p1->next;
656 xmin = min3(p0->x, p1->x, p2->x);
657 ymin = min3(p0->y, p1->y, p2->y);
658 xmax = max3(p0->x, p1->x, p2->x);
659 ymax = max3(p0->y, p1->y, p2->y);
660 canvas_font = lookfont(0, 12); /* get a size 12 font */
661 txt = textsize(canvas_font, strlen(string), string);
662 /* if the box is large enough, put the filename in the four corners */
663 if (xmax - xmin > 2.5*txt.length) {
664 int u,d,marg;
665 u = txt.ascent;
666 d = txt.descent;
667 marg = 6 * ZOOM_FACTOR; /* margin space around text */
668
669 pw_text(canvas_win, xmin+marg, ymin+u+marg, op, line->depth,
670 canvas_font, 0.0, string, DEFAULT, GREEN);
671 pw_text(canvas_win, xmax-txt.length-marg, ymin+u+marg, op, line->depth,
672 canvas_font, 0.0, string, DEFAULT, GREEN);
673 /* do bottom two corners if tall enough */
674 if (ymax - ymin > 3*(u+d)) {
675 pw_text(canvas_win, xmin+marg, ymax-d-marg, op, line->depth,
676 canvas_font, 0.0, string, DEFAULT, GREEN);
677 pw_text(canvas_win, xmax-txt.length-marg, ymax-d-marg, op, line->depth,
678 canvas_font, 0.0, string, DEFAULT, GREEN);
679 }
680 } else {
681 /* only room for one label - center it */
682 x = (xmin + xmax) / 2 - txt.length/display_zoomscale / 2;
683 y = (ymin + ymax) / 2;
684 pw_text(canvas_win, x, y, op, line->depth, canvas_font, 0.0, string, DEFAULT, GREEN);
685 }
686 }
687 /* get first point and coordinates */
688 point = line->points;
689 x = point->x;
690 y = point->y;
691
692 /* is it a single point? */
693 if (line->points->next == NULL) {
694 /* draw but don't fill */
695 pw_point(canvas_win, x, y, op, line->depth,
696 line->thickness, line->pen_color, line->cap_style);
697 /* label the point number above the point */
698 if (appres.shownums && active_layer(line->depth)) {
699 pw_text(canvas_win, x, round(y-3.0/zoomscale), PAINT, line->depth,
700 roman_font, 0.0, "0", RED, COLOR_NONE);
701 }
702 return;
703 }
704
705 /* accumulate the points in an array - start with 50 */
706 init_point_array();
707
708 i=0;
709 for (point = line->points; point != NULL; point = point->next) {
710 x = point->x;
711 y = point->y;
712 /* label the point number above the point */
713 if (appres.shownums && active_layer(line->depth)) {
714 /* if BOX or POLYGON, don't label last point (which is same as first) */
715 if (((line->type == T_BOX || line->type == T_POLYGON) && point->next != NULL) ||
716 (line->type != T_BOX && line->type != T_POLYGON)) {
717 sprintf(bufx,"%d",i++);
718 pw_text(canvas_win, x, round(y-3.0/zoomscale), PAINT, line->depth,
719 roman_font, 0.0, bufx, RED, COLOR_NONE);
720 }
721 }
722 if (!add_point(x, y)) {
723 too_many_points();
724 break;
725 }
726 }
727
728 /* setup clipping so that spline doesn't protrude beyond arrowhead */
729 /* also create the arrowheads */
730 clip_arrows(line,O_POLYLINE,op,0);
731
732 draw_point_array(canvas_win, op, line->depth, line->thickness,
733 line->style, line->style_val, line->join_style,
734 line->cap_style, line->fill_style,
735 line->pen_color, line->fill_color);
736
737 /* restore clipping */
738 set_clip_window(clip_xmin, clip_ymin, clip_xmax, clip_ymax);
739
740 /* draw the arrowheads, if any */
741 if (line->for_arrow)
742 draw_arrow(line, line->for_arrow, farpts, nfpts, farfillpts, nffillpts, op);
743 if (line->back_arrow)
744 draw_arrow(line, line->back_arrow, barpts, nbpts, barfillpts, nbfillpts, op);
745 /* write the depth on the object */
746 debug_depth(line->depth,line->points->x,line->points->y);
747 }
748
draw_arcbox(F_line * line,int op)749 void draw_arcbox(F_line *line, int op)
750 {
751 F_point *point;
752 int xmin, xmax, ymin, ymax;
753 int i;
754
755 point = line->points;
756 xmin = xmax = point->x;
757 ymin = ymax = point->y;
758 i = 0;
759 while (point->next) { /* find lower left (upper-left on screen) */
760 /* and upper right (lower right on screen) */
761 point = point->next;
762 if (point->x < xmin)
763 xmin = point->x;
764 else if (point->x > xmax)
765 xmax = point->x;
766 if (point->y < ymin)
767 ymin = point->y;
768 else if (point->y > ymax)
769 ymax = point->y;
770 /* label the point number above the point */
771 if (appres.shownums && active_layer(line->depth)) {
772 sprintf(bufx,"%d",i++);
773 pw_text(canvas_win, point->x, round(point->y-3.0/zoomscale), PAINT, line->depth,
774 roman_font, 0.0, bufx, RED, COLOR_NONE);
775 }
776 }
777 pw_arcbox(canvas_win, xmin, ymin, xmax, ymax, round(line->radius*ZOOM_FACTOR),
778 op, line->depth, line->thickness, line->style, line->style_val, line->fill_style,
779 line->pen_color, line->fill_color);
780 /* write the depth on the object */
781 debug_depth(line->depth,xmin,ymin);
782 }
783
784 static Boolean
subset(int xmin1,int ymin1,int xmax1,int ymax1,int xmin2,int ymin2,int xmax2,int ymax2)785 subset(int xmin1, int ymin1, int xmax1, int ymax1, int xmin2, int ymin2, int xmax2, int ymax2)
786 {
787 return (xmin2 <= xmin1) && (xmax1 <= xmax2) &&
788 (ymin2 <= ymin1) && (ymax1 <= ymax2);
789 }
790
draw_pic_pixmap(F_line * box,int op)791 void draw_pic_pixmap(F_line *box, int op)
792 {
793 int xmin, ymin;
794 int xmax, ymax;
795 int width, height, rotation;
796 F_pos origin;
797 F_pos opposite;
798 Pixmap clipmask;
799 XGCValues gcv;
800
801 origin.x = ZOOMX(box->points->x);
802 origin.y = ZOOMY(box->points->y);
803 opposite.x = ZOOMX(box->points->next->next->x);
804 opposite.y = ZOOMY(box->points->next->next->y);
805
806 xmin = min2(origin.x, opposite.x);
807 ymin = min2(origin.y, opposite.y);
808 xmax = max2(origin.x, opposite.x);
809 ymax = max2(origin.y, opposite.y);
810
811 if (op == ERASE) {
812 clear_region(xmin, ymin, xmax, ymax);
813 return;
814 }
815 /* width is upper-lower+1 */
816 width = abs(origin.x - opposite.x) + 1;
817 height = abs(origin.y - opposite.y) + 1;
818 rotation = 0;
819 if (origin.x > opposite.x && origin.y > opposite.y)
820 rotation = 180;
821 if (origin.x > opposite.x && origin.y <= opposite.y)
822 rotation = 270;
823 if (origin.x <= opposite.x && origin.y > opposite.y)
824 rotation = 90;
825
826 /* if something has changed regenerate the pixmap */
827 if (box->pic->pixmap == 0 ||
828 box->pic->color != box->pen_color ||
829 box->pic->pix_rotation != rotation ||
830 abs(box->pic->pix_width - width) > 1 || /* rounding makes diff of 1 bit */
831 abs(box->pic->pix_height - height) > 1 ||
832 box->pic->pix_flipped != box->pic->flipped)
833 create_pic_pixmap(box, rotation, width, height, box->pic->flipped);
834
835 if (box->pic->mask) {
836 /* mask is in rectangle (xmin,ymin)...(xmax,ymax)
837 clip to rectangle (clip_xmin,clip_ymin)...(clip_xmax,clip_ymax) */
838 if (subset(xmin, ymin, xmax, ymax, clip_xmin, clip_ymin, clip_xmax, clip_ymax)) {
839 gcv.clip_mask = box->pic->mask;
840 gcv.clip_x_origin = xmin;
841 gcv.clip_y_origin = ymin;
842 clipmask = (Pixmap)0; /* don't need extra clipmask now */
843 }
844 else {
845 /* compute intersection of the two rectangles */
846 GC depth_one_gc;
847 int xxmin, yymin, xxmax, yymax, ww, hh;
848 xxmin = max2(xmin, clip_xmin);
849 xxmax = min2(xmax, clip_xmax);
850 yymin = max2(ymin, clip_ymin);
851 yymax = min2(ymax, clip_ymax);
852 ww = xxmax - xxmin + 1;
853 hh = yymax - yymin + 1;
854 /*
855 The caller should have caught the case that pic and clip rectangle
856 don't overlap. So we may assume ww > 0, hh > 0.
857 */
858 clipmask = XCreatePixmap(tool_d, canvas_win, ww, hh, 1);
859 depth_one_gc = XCreateGC(tool_d, clipmask, (unsigned long) 0, 0);
860 XSetForeground(tool_d, depth_one_gc, 0);
861 XCopyArea(tool_d, box->pic->mask, clipmask, depth_one_gc,
862 xxmin - xmin, yymin - ymin, /* origin source */
863 ww, hh, /* width & height */
864 0, 0); /* origin destination */
865 XFreeGC(tool_d, depth_one_gc);
866 gcv.clip_mask = clipmask;
867 gcv.clip_x_origin = xxmin;
868 gcv.clip_y_origin = yymin;
869 }
870 XChangeGC(tool_d, gccache[op], GCClipMask|GCClipXOrigin|GCClipYOrigin, &gcv);
871 }
872 XCopyArea(tool_d, box->pic->pixmap, canvas_win, gccache[op],
873 0, 0, width, height, xmin, ymin);
874 if (box->pic->mask) {
875 gcv.clip_mask = 0;
876 XChangeGC(tool_d, gccache[op], GCClipMask, &gcv);
877 if (clipmask)
878 XFreePixmap(tool_d, clipmask);
879 /* restore clipping */
880 set_clip_window(clip_xmin, clip_ymin, clip_xmax, clip_ymax);
881 }
882 XFlush(tool_d);
883 }
884
885 /*
886 * The input to this routine is the bitmap read from the source
887 * image file. That input bitmap has an arbitrary number of rows
888 * and columns. This routine re-samples the input bitmap creating
889 * an output bitmap of dimensions width-by-height. This output
890 * bitmap is made into a Pixmap for display purposes.
891 */
892
893 #define ALLOC_PIC_ERR "Can't alloc memory for image: %s"
894
create_pic_pixmap(F_line * box,int rotation,int width,int height,int flipped)895 void create_pic_pixmap(F_line *box, int rotation, int width, int height, int flipped)
896 {
897 int cwidth, cheight;
898 int i,j,k;
899 int bwidth;
900 unsigned char *data, *tdata, *mask;
901 int bbytes;
902 int ibit, jbit;
903 int wbit;
904 int fg, bg;
905 size_t jnb, nbytes;
906 XImage *image;
907 Boolean type1,hswap,vswap;
908
909 /* this could take a while */
910 set_temp_cursor(wait_cursor);
911 if (box->pic->pixmap != 0)
912 XFreePixmap(tool_d, box->pic->pixmap);
913 if (box->pic->mask != 0)
914 XFreePixmap(tool_d, box->pic->mask);
915
916 if (appres.DEBUG)
917 fprintf(stderr,"Scaling pic pixmap to %dx%d pixels\n",width,height);
918
919 cwidth = box->pic->pic_cache->bit_size.x; /* current width, height */
920 cheight = box->pic->pic_cache->bit_size.y;
921
922 box->pic->color = box->pen_color;
923 box->pic->pix_rotation = rotation;
924 box->pic->pix_width = width;
925 box->pic->pix_height = height;
926 box->pic->pix_flipped = flipped;
927 box->pic->pixmap = (Pixmap) 0;
928 box->pic->mask = (Pixmap) 0;
929
930 mask = (unsigned char *) 0;
931
932 /* create a new bitmap at the specified size (requires interpolation) */
933
934 /* MONOCHROME display OR XBM */
935 if (box->pic->pic_cache->numcols == 0) {
936 nbytes = (width + 7) / 8;
937 bbytes = (cwidth + 7) / 8;
938 if ((data = (unsigned char *) malloc(nbytes * height)) == NULL) {
939 file_msg(ALLOC_PIC_ERR, box->pic->pic_cache->file);
940 return;
941 }
942 if ((tdata = (unsigned char *) malloc(nbytes)) == NULL) {
943 file_msg(ALLOC_PIC_ERR, box->pic->pic_cache->file);
944 free(data);
945 return;
946 }
947 memset(data, 0, nbytes * height);
948 if ((!flipped && (rotation == 0 || rotation == 180)) ||
949 (flipped && !(rotation == 0 || rotation == 180))) {
950 for (j = 0; j < height; j++) {
951 /* check if user pressed cancel button */
952 if (check_cancel())
953 break;
954 jbit = cheight * j / height * bbytes;
955 for (i = 0; i < width; i++) {
956 ibit = cwidth * i / width;
957 wbit = (unsigned char) *(box->pic->pic_cache->bitmap + jbit + ibit / 8);
958 if (wbit & (1 << (7 - (ibit & 7))))
959 *(data + j * nbytes + i / 8) += (1 << (i & 7));
960 }
961 }
962 } else {
963 for (j = 0; j < height; j++) {
964 /* check if user pressed cancel button */
965 if (check_cancel())
966 break;
967 ibit = cwidth * j / height;
968 for (i = 0; i < width; i++) {
969 jbit = cheight * i / width * bbytes;
970 wbit = (unsigned char) *(box->pic->pic_cache->bitmap + jbit + ibit / 8);
971 if (wbit & (1 << (7 - (ibit & 7))))
972 *(data + (height - j - 1) * nbytes + i / 8) += (1 << (i & 7));
973 }
974 }
975 }
976
977 /* horizontal swap */
978 if (rotation == 180 || rotation == 270)
979 for (j = 0; j < height; j++) {
980 /* check if user pressed cancel button */
981 if (check_cancel())
982 break;
983 jnb = j*nbytes;
984 memset(tdata, 0, nbytes);
985 for (i = 0; i < width; i++)
986 if (*(data + jnb + (width - i - 1) / 8) & (1 << ((width - i - 1) & 7)))
987 *(tdata + i / 8) += (1 << (i & 7));
988 memcpy(data + j * nbytes, tdata, nbytes);
989 }
990
991 /* vertical swap */
992 if ((!flipped && (rotation == 180 || rotation == 270)) ||
993 (flipped && !(rotation == 180 || rotation == 270))) {
994 for (j = 0; j < (height + 1) / 2; j++) {
995 jnb = j*nbytes;
996 memcpy(tdata, data + jnb, nbytes);
997 memmove(data + jnb, data + (height - j - 1) * nbytes, nbytes);
998 memcpy(data + (height - j - 1) * nbytes, tdata, nbytes);
999 }
1000 }
1001
1002 if (box->pic->pic_cache->subtype == T_PIC_XBM) {
1003 fg = x_color(box->pen_color); /* xbm, use object pen color */
1004 bg = x_bg_color.pixel;
1005 } else if (box->pic->pic_cache->subtype == T_PIC_EPS ||
1006 box->pic->pic_cache->subtype == T_PIC_PDF) {
1007 fg = black_color.pixel; /* pbm from gs is inverted */
1008 bg = white_color.pixel;
1009 } else {
1010 fg = white_color.pixel; /* gif, xpm after map_to_mono */
1011 bg = black_color.pixel;
1012 }
1013
1014 box->pic->pixmap = XCreatePixmapFromBitmapData(tool_d, canvas_win,
1015 (char *)data, width, height, fg,bg, tool_dpth);
1016 free(data);
1017 free(tdata);
1018
1019 /* EPS, PCX, XPM, GIF, PNG or JPEG on *COLOR* display */
1020 /* It is important to note that the Cmap pixels are unsigned long. */
1021 /* Therefore all manipulation of the image data should be as unsigned long. */
1022 /* bpl = bytes per line */
1023
1024 } else {
1025 unsigned char *pixel, *cpixel, *dst, *src, tmp;
1026 int bpl, cbpp, cbpl;
1027 unsigned int *Lpixel;
1028 unsigned short *Spixel;
1029 unsigned char *Cpixel;
1030 struct Cmap *cmap = box->pic->pic_cache->cmap;
1031 Boolean endian;
1032 unsigned char byte;
1033
1034 /* figure what endian machine this is (big=True or little=False) */
1035 endian = True;
1036 bpl = 1;
1037 Cpixel = (unsigned char *) &bpl;
1038 /* look at first byte of integer */
1039 if (Cpixel[0] == 1)
1040 endian = False;
1041
1042 /*
1043 * See comments (around XPutPixel() ?) in
1044 * http://gitlab.freedesktop.org/xorg/libX11/src/ImUtil.c,
1045 * where it is assumed that all formats have bits_per_pixel <= 32,
1046 * where bits_per_pixel is a field in struct XVisualInfo.
1047 */
1048 if (tool_vclass == TrueColor && image_bpp == 4 &&
1049 box->pic->pic_cache->numcols <= 0)
1050 /* no colormap, argb quadruples */
1051 cbpp = 4;
1052 else
1053 cbpp = 1;
1054 cbpl = cwidth * cbpp;
1055 bpl = width * image_bpp;
1056 if ((data = malloc(bpl * height)) == NULL) {
1057 file_msg(ALLOC_PIC_ERR,box->pic->pic_cache->file);
1058 return;
1059 }
1060 /* allocate mask for any transparency information */
1061 if (box->pic->pic_cache->subtype == T_PIC_GIF &&
1062 box->pic->pic_cache->transp != TRANSP_NONE) {
1063 if ((mask = (unsigned char *) malloc((width+7)/8 * height)) == NULL) {
1064 file_msg(ALLOC_PIC_ERR,box->pic->pic_cache->file);
1065 free(data);
1066 return;
1067 }
1068 /* set all bits in mask */
1069 for (i = (width+7)/8 * height - 1; i >= 0; i--)
1070 *(mask+i)= (unsigned char) 255;
1071 }
1072 bwidth = (width+7)/8;
1073 memset(data, 0, bpl * height);
1074
1075 type1 = False;
1076 hswap = False;
1077 vswap = False;
1078
1079 if ((!flipped && (rotation == 0 || rotation == 180)) ||
1080 (flipped && !(rotation == 0 || rotation == 180)))
1081 type1 = True;
1082 /* horizontal swap */
1083 if (rotation == 180 || rotation == 270)
1084 hswap = True;
1085 /* vertical swap */
1086 if ((!flipped && (rotation == 90 || rotation == 180)) ||
1087 ( flipped && (rotation == 90 || rotation == 180)))
1088 vswap = True;
1089
1090 for( j=0; j<height; j++ ) {
1091 /* check if user pressed cancel button */
1092 if (check_cancel())
1093 break;
1094
1095 if (type1) {
1096 src = box->pic->pic_cache->bitmap +
1097 (j * cheight / height) * cbpl;
1098 dst = data + (j * bpl);
1099 } else {
1100 src = box->pic->pic_cache->bitmap + (j * cwidth /
1101 height) * cbpp;
1102 dst = data + (j * bpl);
1103 }
1104
1105 pixel = dst;
1106 for( i=0; i<width; i++ ) {
1107 if (type1) {
1108 cpixel = src + (i * cwidth / width) * cbpp;
1109 } else {
1110 cpixel = src + (i * cheight / width * cwidth) * cbpp;
1111 }
1112 /* if this pixel is the transparent color then clear the mask pixel */
1113 if (box->pic->pic_cache->transp != TRANSP_NONE &&
1114 (*cpixel==(unsigned char) box->pic->pic_cache->transp)) {
1115 if (type1) {
1116 if (hswap) {
1117 if (vswap)
1118 clr_mask_bit(height-j-1,width-i-1,bwidth,mask);
1119 else
1120 clr_mask_bit(j,width-i-1,bwidth,mask);
1121 } else {
1122 if (vswap)
1123 clr_mask_bit(height-j-1,i,bwidth,mask);
1124 else
1125 clr_mask_bit(j,i,bwidth,mask);
1126 }
1127 } else {
1128 if (!vswap) {
1129 if (hswap)
1130 clr_mask_bit(j,width-i-1,bwidth,mask);
1131 else
1132 clr_mask_bit(j,i,bwidth,mask);
1133 } else {
1134 if (hswap)
1135 clr_mask_bit(height-j-1,width-i-1,bwidth,mask);
1136 else
1137 clr_mask_bit(height-j-1,i,bwidth,mask);
1138 }
1139 } /* type */
1140 }
1141 if (image_bpp == 4) {
1142 Lpixel = (unsigned int *) pixel;
1143 if (box->pic->pic_cache->numcols <= 0)
1144 /* && tool_v == TrueColor */
1145 /* no colormap, rgb color */
1146 *Lpixel = *(unsigned int *)cpixel;
1147 else
1148 *Lpixel = (unsigned int)cmap[*cpixel].pixel;
1149 /* swap the 4 bytes on big-endian machines */
1150 if (endian) {
1151 Cpixel = (unsigned char *) Lpixel;
1152 byte = Cpixel[0]; Cpixel[0] = Cpixel[3]; Cpixel[3] = byte;
1153 byte = Cpixel[1]; Cpixel[1] = Cpixel[2]; Cpixel[2] = byte;
1154 }
1155 } else if (image_bpp == 3) {
1156 unsigned char *p;
1157 p = (unsigned char *)&(cmap[*cpixel].pixel);
1158 /* note which endian */
1159 if (endian) {
1160 Cpixel = (unsigned char *) pixel+2;
1161 *Cpixel-- = *p++;
1162 *Cpixel-- = *p++;
1163 *Cpixel = *p;
1164 } else {
1165 Cpixel = (unsigned char *) pixel;
1166 *Cpixel++ = *p++;
1167 *Cpixel++ = *p++;
1168 *Cpixel = *p;
1169 }
1170 } else if (image_bpp == 2) {
1171 Spixel = (unsigned short *) pixel;
1172 *Spixel = (unsigned short)cmap[*cpixel].pixel;
1173 /* swap the 2 bytes on big-endian machines */
1174 if (endian) {
1175 Cpixel = (unsigned char *) Lpixel;
1176 byte = Cpixel[0]; Cpixel[0] = Cpixel[1]; Cpixel[1] = byte;
1177 }
1178 } else {
1179 Cpixel = (unsigned char *) pixel;
1180 *Cpixel = (unsigned char)cmap[*cpixel].pixel;
1181 }
1182 /* next pixel position */
1183 pixel += image_bpp;
1184 }
1185 }
1186
1187 /* horizontal swap */
1188 if (hswap) {
1189 for( j=0; j<height; j++ ) {
1190 /* check if user pressed cancel button */
1191 if (check_cancel())
1192 break;
1193 dst = data + (j * bpl);
1194 src = dst + ((width - 1) * image_bpp);
1195 for( i=0; i<width/2; i++, src -= 2*image_bpp ) {
1196 for( k=0; k<image_bpp; k++, dst++, src++ ) {
1197 tmp = *dst;
1198 *dst = *src;
1199 *src = tmp;
1200 }
1201 }
1202 }
1203 }
1204
1205 /* vertical swap */
1206 if (vswap) {
1207 for( i=0; i<width; i++ ) {
1208 dst = data + (i * image_bpp);
1209 src = dst + ((height - 1) * bpl);
1210 for( j=0; j<height/2; j++, dst += (width-1)*image_bpp, src -= (width+1)*image_bpp ) {
1211 for( k=0; k<image_bpp; k++, dst++, src++ ) {
1212 tmp = *dst;
1213 *dst = *src;
1214 *src = tmp;
1215 }
1216 }
1217 }
1218 }
1219
1220 image = XCreateImage(tool_d, tool_v, tool_dpth,
1221 ZPixmap, 0, (char *)data, width, height, 8, 0);
1222 box->pic->pixmap = XCreatePixmap(tool_d, canvas_win,
1223 width, height, tool_dpth);
1224 if (image->byte_order == MSBFirst) {
1225 image->byte_order = LSBFirst;
1226 _XInitImageFuncPtrs(image);
1227 }
1228 if (image->bitmap_bit_order == MSBFirst) {
1229 image->bitmap_bit_order = LSBFirst;
1230 _XInitImageFuncPtrs(image);
1231 }
1232 XPutImage(tool_d, box->pic->pixmap, pic_gc, image, 0, 0, 0, 0, width, height);
1233 XDestroyImage(image);
1234 /* make the clipmask to do the GIF transparency */
1235 if (mask) {
1236 box->pic->mask = XCreateBitmapFromData(tool_d, tool_w, (char*) mask,
1237 width, height);
1238 free(mask);
1239 }
1240 }
1241 reset_cursor();
1242 }
1243
1244 /* clear bit at row "r", column "c" in array "mask" of width "width" */
1245
1246 static unsigned char bits[8] = { 1,2,4,8,16,32,64,128 };
1247
clr_mask_bit(int r,int c,int bwidth,unsigned char * mask)1248 void clr_mask_bit(int r, int c, int bwidth, unsigned char *mask)
1249 {
1250 int byte;
1251 unsigned char bit;
1252
1253 byte = r*bwidth + c/8;
1254 bit = c % 8;
1255 mask[byte] &= ~bits[bit];
1256 }
1257
1258 /*********************** TEXT ***************************/
1259
1260 static char *hidden_text_string = "<<>>";
1261
draw_text(F_text * text,int op)1262 void draw_text(F_text *text, int op)
1263 {
1264 PR_SIZE size;
1265 int x,y;
1266 int xmin, ymin, xmax, ymax;
1267 int x1,y1, x2,y2, x3,y3, x4,y4;
1268 double cost, sint;
1269
1270 if (text->zoom != zoomscale || text->fontstruct == (XFontStruct*) 0)
1271 reload_text_fstruct(text);
1272 text_bound(text, &xmin, &ymin, &xmax, &ymax,
1273 &x1,&y1, &x2,&y2, &x3,&y3, &x4,&y4);
1274
1275 if (!overlapping(ZOOMX(xmin), ZOOMY(ymin), ZOOMX(xmax), ZOOMY(ymax),
1276 clip_xmin, clip_ymin, clip_xmax, clip_ymax))
1277 return;
1278
1279 /* outline the text bounds in red if debug resource is set */
1280 if (appres.DEBUG) {
1281 pw_vector(canvas_win, x1, y1, x2, y2, op, 1, RUBBER_LINE, 0.0, RED);
1282 pw_vector(canvas_win, x2, y2, x3, y3, op, 1, RUBBER_LINE, 0.0, RED);
1283 pw_vector(canvas_win, x3, y3, x4, y4, op, 1, RUBBER_LINE, 0.0, RED);
1284 pw_vector(canvas_win, x4, y4, x1, y1, op, 1, RUBBER_LINE, 0.0, RED);
1285 }
1286
1287 x = text->base_x;
1288 y = text->base_y;
1289 cost = cos(text->angle);
1290 sint = sin(text->angle);
1291 if (text->type == T_CENTER_JUSTIFIED || text->type == T_RIGHT_JUSTIFIED) {
1292 size = textsize(text->fontstruct, strlen(text->cstring),
1293 text->cstring);
1294 size.length = size.length/display_zoomscale;
1295 if (text->type == T_CENTER_JUSTIFIED) {
1296 x = round(x-cost*size.length/2);
1297 y = round(y+sint*size.length/2);
1298 } else { /* T_RIGHT_JUSTIFIED */
1299 x = round(x-cost*size.length);
1300 y = round(y+sint*size.length);
1301 }
1302 }
1303 if (hidden_text(text)) {
1304 pw_text(canvas_win, x, y, op, text->depth, lookfont(0,12),
1305 text->angle, hidden_text_string, DEFAULT, COLOR_NONE);
1306 } else {
1307 /* if size is less than the displayable size, Greek it by drawing a DARK gray line,
1308 UNLESS the depth is inactive in which case draw it in MED_GRAY */
1309 if (text->size*display_zoomscale < MIN_X_FONT_SIZE) {
1310 x1 = (x1+x4)/2;
1311 x2 = (x2+x3)/2;
1312 y1 = (y1+y4)/2;
1313 y2 = (y2+y3)/2;
1314 greek_text(text, x1, y1, x2, y2);
1315 } else {
1316 /* Otherwise, draw the text normally */
1317 pw_text(canvas_win, x, y, op, text->depth, text->fontstruct,
1318 text->angle, text->cstring, text->color, COLOR_NONE);
1319 }
1320 }
1321
1322 /* write the depth on the object */
1323 debug_depth(text->depth,x,y);
1324 }
1325
1326 /*
1327 * Draw text as "Greeked", from (x1, y1) to (x2, y2), meaning as a dashed gray line.
1328 * This is done when the text would be too small to read anyway.
1329 */
1330
greek_text(F_text * text,int x1,int y1,int x2,int y2)1331 void greek_text(F_text *text, int x1, int y1, int x2, int y2)
1332 {
1333 int color;
1334 int lensofar, lenword, lenspace;
1335 int xs, ys, xe, ye;
1336 float dx, dy;
1337 char *cp;
1338
1339 if (text->depth < MAX_DEPTH+1 && !active_layer(text->depth))
1340 color = MED_GRAY;
1341 else
1342 color = DARK_GRAY;
1343
1344 cp = text->cstring;
1345
1346 /* get unit dx, dy */
1347 dx = (float)(x2-x1)/strlen(cp);
1348 dy = (float)(y2-y1)/strlen(cp);
1349
1350 lensofar = 0;
1351 while (*cp) {
1352 lenword = 0;
1353 /* count chars in this word */
1354 while (*cp) {
1355 if (*cp == ' ')
1356 break;
1357 lenword++;
1358 cp++;
1359 }
1360 /* now count how many spaces follow it */
1361 lenspace = 0;
1362 while (*cp) {
1363 if (*cp != ' ')
1364 break;
1365 lenspace++;
1366 cp++;
1367 }
1368 xs = x1 + dx*lensofar;
1369 ys = y1 + dy*lensofar;
1370 xe = xs + dx*lenword;
1371 ye = ys + dy*lenword;
1372 pw_vector(canvas_win, xs, ys, xe, ye, PAINT, 1, RUBBER_LINE, 0.0, color);
1373 /* add length of this word and space(s) to lensofar */
1374 lensofar = lensofar + lenword + lenspace;
1375 }
1376 }
1377
1378 /*********************** COMPOUND ***************************/
1379
1380 void
draw_compoundelements(F_compound * c,int op)1381 draw_compoundelements(F_compound *c, int op)
1382 {
1383 F_line *l;
1384 F_spline *s;
1385 F_ellipse *e;
1386 F_text *t;
1387 F_arc *a;
1388 F_compound *c1;
1389
1390 if (!overlapping(ZOOMX(c->nwcorner.x), ZOOMY(c->nwcorner.y),
1391 ZOOMX(c->secorner.x), ZOOMY(c->secorner.y),
1392 clip_xmin, clip_ymin, clip_xmax, clip_ymax))
1393 return;
1394
1395 for (l = c->lines; l != NULL; l = l->next) {
1396 if (active_layer(l->depth))
1397 draw_line(l, op);
1398 }
1399 for (s = c->splines; s != NULL; s = s->next) {
1400 if (active_layer(s->depth))
1401 draw_spline(s, op);
1402 }
1403 for (a = c->arcs; a != NULL; a = a->next) {
1404 if (active_layer(a->depth))
1405 draw_arc(a, op);
1406 }
1407 for (e = c->ellipses; e != NULL; e = e->next) {
1408 if (active_layer(e->depth))
1409 draw_ellipse(e, op);
1410 }
1411 for (t = c->texts; t != NULL; t = t->next) {
1412 if (active_layer(t->depth))
1413 draw_text(t, op);
1414 }
1415 for (c1 = c->compounds; c1 != NULL; c1 = c1->next) {
1416 if (any_active_in_compound(c1))
1417 draw_compoundelements(c1, op);
1418 }
1419 }
1420
1421 /*************************** ARROWS *****************************
1422
1423 compute_arcarrow_angle - Computes a point on a line which is a chord
1424 to the arc specified by center (x1,y1) and endpoint (x2,y2),
1425 where the chord intersects the arc arrow->ht from the endpoint.
1426
1427 May give strange values if the arrow.ht is larger than about 1/4 of
1428 the circumference of a circle on which the arc lies.
1429
1430 ****************************************************************/
1431
compute_arcarrow_angle(float x1,float y1,int x2,int y2,int direction,F_arrow * arrow,int * x,int * y)1432 void compute_arcarrow_angle(float x1, float y1, int x2, int y2, int direction, F_arrow *arrow, int *x, int *y)
1433 {
1434 double r, alpha, beta, dy, dx;
1435 double lpt,h;
1436
1437 dy=y2-y1;
1438 dx=x2-x1;
1439 r=sqrt(dx*dx+dy*dy);
1440
1441 h = (double) arrow->ht*ZOOM_FACTOR;
1442 /* lpt is the amount the arrowhead extends beyond the end of the line */
1443 lpt = arrow->thickness/2.0/(arrow->wd/h/2.0);
1444 /* add this to the length */
1445 h += lpt;
1446
1447 /* radius too small for this method, use normal method */
1448 if (h > 2.0*r) {
1449 compute_normal(x1,y1,x2,y2,direction,x,y);
1450 return;
1451 }
1452
1453 beta=atan2(dy,dx);
1454 if (direction) {
1455 alpha=2*asin(h/2.0/r);
1456 } else {
1457 alpha=-2*asin(h/2.0/r);
1458 }
1459
1460 *x=round(x1+r*cos(beta+alpha));
1461 *y=round(y1+r*sin(beta+alpha));
1462 }
1463
1464 /* temporary error handler - see call to XSetRegion in clip_arrows below */
1465
tempXErrorHandler(Display * display,XErrorEvent * event)1466 int tempXErrorHandler (Display *display, XErrorEvent *event)
1467 {
1468 return 0;
1469 }
1470
1471
1472 /****************************************************************
1473
1474 clip_arrows - calculate a clipping region which is the current
1475 clipping area minus the polygons at the arrowheads.
1476
1477 This will prevent the object (line, spline etc.) from protruding
1478 on either side of the arrowhead Also calculate the arrowheads
1479 themselves and put the outline polygons in farpts[nfpts] for forward
1480 arrow and barpts[nbpts] for backward arrow, and the fill areas in
1481 farfillpts[nffillpts] for forward arrow and barfillpts[nbfillpts]
1482 for backward arrow.
1483
1484 The points[] array must already have the points for the object
1485 being drawn (spline, line etc), and npoints, the number of points.
1486
1487 "skip" points are skipped from each end of the points[] array (for splines)
1488 ****************************************************************/
1489
clip_arrows(F_line * obj,int objtype,int op,int skip)1490 void clip_arrows(F_line *obj, int objtype, int op, int skip)
1491 {
1492 Region mainregion, newregion;
1493 Region region;
1494 XPoint xpts[50];
1495 int x, y;
1496 zXPoint clippts[50];
1497 int i, j, n, nclippts;
1498
1499
1500 if (obj->for_arrow || obj->back_arrow) {
1501 /* start with current clipping area - maybe we won't have to draw anything */
1502 xpts[0].x = clip_xmin;
1503 xpts[0].y = clip_ymin;
1504 xpts[1].x = clip_xmax;
1505 xpts[1].y = clip_ymin;
1506 xpts[2].x = clip_xmax;
1507 xpts[2].y = clip_ymax;
1508 xpts[3].x = clip_xmin;
1509 xpts[3].y = clip_ymax;
1510 mainregion = XPolygonRegion(xpts, 4, WindingRule);
1511 }
1512
1513 if (skip > npoints-2)
1514 skip = 0;
1515
1516 /* get points for any forward arrowhead */
1517 if (obj->for_arrow) {
1518 x = points[npoints-skip-2].x;
1519 y = points[npoints-skip-2].y;
1520 if (objtype == O_ARC) {
1521 F_arc *a = (F_arc *) obj;
1522 compute_arcarrow_angle(a->center.x, a->center.y, a->point[2].x,
1523 a->point[2].y, a->direction,
1524 a->for_arrow, &x, &y);
1525 }
1526 calc_arrow(x, y, points[npoints-1].x, points[npoints-1].y, obj->thickness,
1527 obj->for_arrow, farpts, &nfpts, farfillpts, &nffillpts, clippts, &nclippts);
1528 if (nclippts) {
1529 /* set clipping in scaled space */
1530 for (i=0; i < nclippts; i++) {
1531 xpts[i].x = ZOOMX(clippts[i].x);
1532 xpts[i].y = ZOOMY(clippts[i].y);
1533 }
1534 n = i;
1535 /* draw the clipping area for debugging */
1536 if (appres.DEBUG) {
1537 for (i=0; i<n; i++) {
1538 if (i==n-1)
1539 j=0;
1540 else
1541 j=i+1;
1542 pw_vector(canvas_win,xpts[i].x,xpts[i].y,xpts[j].x,xpts[j].y,op,1,
1543 PANEL_LINE,0.0,RED);
1544 }
1545 }
1546 region = XPolygonRegion(xpts, n, WindingRule);
1547 newregion = XCreateRegion();
1548 XSubtractRegion(mainregion, region, newregion);
1549 XDestroyRegion(region);
1550 XDestroyRegion(mainregion);
1551 mainregion=newregion;
1552 }
1553 }
1554
1555 /* get points for any backward arrowhead */
1556 if (obj->back_arrow) {
1557 x = points[skip+1].x;
1558 y = points[skip+1].y;
1559 if (objtype == O_ARC) {
1560 F_arc *a = (F_arc *) obj;
1561 compute_arcarrow_angle(a->center.x, a->center.y, a->point[0].x,
1562 a->point[0].y, a->direction ^ 1,
1563 a->back_arrow, &x, &y);
1564 }
1565 calc_arrow(x, y, points[0].x, points[0].y, obj->thickness,
1566 obj->back_arrow, barpts, &nbpts, barfillpts, &nbfillpts, clippts, &nclippts);
1567 if (nclippts) {
1568 /* set clipping in scaled space */
1569 for (i=0; i < nclippts; i++) {
1570 xpts[i].x = ZOOMX(clippts[i].x);
1571 xpts[i].y = ZOOMY(clippts[i].y);
1572 }
1573 n = i;
1574 /* draw the clipping area for debugging */
1575 if (appres.DEBUG) {
1576 int j;
1577 for (i=0; i<n; i++) {
1578 if (i==n-1)
1579 j=0;
1580 else
1581 j=i+1;
1582 pw_vector(canvas_win,xpts[i].x,xpts[i].y,xpts[j].x,xpts[j].y,op,1,
1583 PANEL_LINE,0.0,RED);
1584 }
1585 }
1586 region = XPolygonRegion(xpts, n, WindingRule);
1587 newregion = XCreateRegion();
1588 XSubtractRegion(mainregion, region, newregion);
1589 XDestroyRegion(region);
1590 XDestroyRegion(mainregion);
1591 mainregion=newregion;
1592 }
1593 }
1594 /* now set the clipping region for the subsequent drawing of the object */
1595 if (obj->for_arrow || obj->back_arrow) {
1596 /* install a temporary error handler to ignore any BadMatch error
1597 from the buggy R5 Xlib XSetRegion() */
1598 XSetErrorHandler (tempXErrorHandler);
1599 XSetRegion(tool_d, gccache[op], mainregion);
1600 /* restore original error handler */
1601 if (!appres.DEBUG)
1602 XSetErrorHandler(X_error_handler);
1603 XDestroyRegion(mainregion);
1604 }
1605 }
1606
1607 /****************************************************************
1608
1609 calc_arrow - calculate arrowhead points heading from (x1, y1) to (x2, y2)
1610
1611 |\
1612 | \
1613 | \
1614 (x1,y1) +---------------| \+ (x2, y2)
1615 | /
1616 | /
1617 | /
1618 |/
1619
1620 Fills points[] array with npoints arrowhead *outline* coordinates and
1621 fillpoints[] array with nfillpoints points for the part to be filled *IF*
1622 it is a special arrowhead that has a different fill area than the outline.
1623
1624 Otherwise, the points[] array is also used to fill the arrowhead in draw_arrow()
1625 The linethick param is the thickness of the *main line/spline/arc*,
1626 not the arrowhead.
1627
1628 The clippts[] array is filled with the clip area so that the line won't
1629 protrude through the arrowhead.
1630
1631 ****************************************************************/
1632
1633 #define ROTX(x,y) (x)*cosa + (y)*sina + xa
1634 #define ROTY(x,y) -(x)*sina + (y)*cosa + ya
1635
1636 #define ROTX2(x,y) (x)*cosa + (y)*sina + x2
1637 #define ROTY2(x,y) -(x)*sina + (y)*cosa + y2
1638
1639 #define ROTXC(x,y) (x)*cosa + (y)*sina + fix_x
1640 #define ROTYC(x,y) -(x)*sina + (y)*cosa + fix_y
1641
calc_arrow(int x1,int y1,int x2,int y2,int linethick,F_arrow * arrow,zXPoint * points,int * npoints,zXPoint * fillpoints,int * nfillpoints,zXPoint * clippts,int * nclippts)1642 void calc_arrow(int x1, int y1, int x2, int y2, int linethick, F_arrow *arrow, zXPoint *points, int *npoints, zXPoint *fillpoints, int *nfillpoints, zXPoint *clippts, int *nclippts)
1643 {
1644 double x, y, xb, yb, dx, dy, l, sina, cosa;
1645 double mx, my;
1646 double ddx, ddy, lpt, tipmv;
1647 double alpha;
1648 double miny, maxy;
1649 int xa, ya, xs, ys;
1650 double wd = (double) arrow->wd*ZOOM_FACTOR;
1651 double len = (double) arrow->ht*ZOOM_FACTOR;
1652 double th = arrow->thickness*ZOOM_FACTOR;
1653 double radius;
1654 double angle, init_angle, rads;
1655 double fix_x, fix_y;
1656 int type, style, indx, tip;
1657 int i, np;
1658 int offset, halfthick;
1659
1660 /* to enlarge the clip area in case the line is thick */
1661 halfthick = linethick * ZOOM_FACTOR/2 + 1;
1662
1663 /* types = 0...10 */
1664 type = arrow->type;
1665 /* style = 0 (unfilled) or 1 (filled) */
1666 style = arrow->style;
1667 /* index into shape array */
1668 indx = 2*type + style;
1669
1670 *npoints = *nfillpoints = 0;
1671 *nclippts = 0;
1672 dx = x2 - x1;
1673 dy = y1 - y2;
1674 /* return now if arrowhead width or length is 0 or line has zero length */
1675 if (wd == 0 || len == 0 || (dx==0 && dy==0))
1676 return;
1677
1678 /* lpt is the amount the arrowhead extends beyond the end of the
1679 line because of the sharp point (miter join) */
1680 tipmv = arrow_shapes[indx].tipmv;
1681 lpt = 0.0;
1682 if (tipmv > 0.0)
1683 lpt = th * sqrt(wd*wd + tipmv*tipmv*len*len) / 2.0 / wd;
1684 else if (tipmv == 0.0)
1685 lpt = th / 2.0; /* types which have blunt end */
1686 /* (Don't adjust those with tipmv < 0) */
1687
1688 /* alpha is the angle the line is relative to horizontal */
1689 alpha = atan2(dy,-dx);
1690
1691 /* ddx, ddy is amount to move end of line back so that arrowhead point
1692 ends where line used to */
1693 ddx = lpt * cos(alpha);
1694 ddy = lpt * sin(alpha);
1695
1696 /* move endpoint of line back */
1697 mx = x2 + ddx;
1698 my = y2 + ddy;
1699
1700 l = sqrt(dx * dx + dy * dy);
1701 sina = dy / l;
1702 cosa = dx / l;
1703 xb = mx * cosa - my * sina;
1704 yb = mx * sina + my * cosa;
1705
1706 /* (xa,ya) is the rotated endpoint (used in ROTX and ROTY macros) */
1707 xa = round(xb * cosa + yb * sina);
1708 ya = round(-xb * sina + yb * cosa);
1709
1710 miny = 100000.0;
1711 maxy = -100000.0;
1712
1713 if (type == 5 || type == 6) {
1714 /*
1715 * CIRCLE and HALF-CIRCLE arrowheads
1716 *
1717 * We approximate circles with (40+zoom)/4 points
1718 */
1719
1720 /* use original dx, dy to get starting angle */
1721 init_angle = compute_angle(dx, dy);
1722
1723 /* (xs,ys) is a point the length of the arrowhead BACK from
1724 the end of the shaft */
1725 /* for the half circle, use 0.0 */
1726 xs = (xb-(type==5? len: 0.0)) * cosa + yb * sina + 0.5;
1727 ys = -(xb-(type==5? len: 0.0)) * sina + yb * cosa + 0.5;
1728
1729 /* calc new (dx, dy) from moved endpoint to (xs, ys) */
1730 dx = mx - xs;
1731 dy = my - ys;
1732 /* radius */
1733 radius = len/2.0;
1734 fix_x = xs + (dx / (double) 2.0);
1735 fix_y = ys + (dy / (double) 2.0);
1736 /* choose number of points for circle - 40+zoom/4 points */
1737 np = round(display_zoomscale/4.0) + 40;
1738
1739 if (type == 5) {
1740 /* full circle */
1741 init_angle = 5.0*M_PI_2 - init_angle;
1742 rads = M_2PI;
1743 } else {
1744 /* half circle */
1745 init_angle = 3.0*M_PI_2 - init_angle;
1746 rads = M_PI;
1747 }
1748
1749 /* draw the half or full circle */
1750 for (i = 0; i < np; i++) {
1751 angle = init_angle - (rads * (double) i / (double) (np-1));
1752 x = fix_x + round(radius * cos(angle));
1753 points[*npoints].x = x;
1754 y = fix_y + round(radius * sin(angle));
1755 points[*npoints].y = y;
1756 (*npoints)++;
1757 }
1758
1759 /* set clipping to a box at least as large as the line thickness
1760 or diameter of the circle, whichever is larger */
1761 /* 4 points in clip box */
1762 miny = min2(-halfthick, -radius-th/2.0);
1763 maxy = max2( halfthick, radius+th/2.0);
1764
1765 i=0;
1766 /* start at new endpoint of line */
1767 clippts[i].x = ROTXC(0, -radius-th/2.0);
1768 clippts[i].y = ROTYC(0, -radius-th/2.0);
1769 i++;
1770 clippts[i].x = ROTXC(0, miny);
1771 clippts[i].y = ROTYC(0, miny);
1772 i++;
1773 /* add halfthick in case the line cap style is Round or Projecting */
1774 clippts[i].x = ROTXC(radius+th/2.0+halfthick, miny);
1775 clippts[i].y = ROTYC(radius+th/2.0+halfthick, miny);
1776 i++;
1777 /* add halfthick in case the line cap style is Round or Projecting */
1778 clippts[i].x = ROTXC(radius+th/2.0+halfthick, maxy);
1779 clippts[i].y = ROTYC(radius+th/2.0+halfthick, maxy);
1780 i++;
1781 clippts[i].x = ROTXC(0, maxy);
1782 clippts[i].y = ROTYC(0, maxy);
1783 i++;
1784 *nclippts = i;
1785
1786 } else {
1787 /*
1788 * ALL OTHER HEADS
1789 */
1790
1791 *npoints = arrow_shapes[indx].numpts;
1792 /* we'll shift the half arrowheads down by the difference of the main line thickness
1793 and the arrowhead thickness to make it flush with the main line */
1794 if (arrow_shapes[indx].half)
1795 offset = ZOOM_FACTOR * (linethick - arrow->thickness)/2;
1796 else
1797 offset = 0;
1798
1799 /* fill the points array with the outline */
1800 for (i=0; i<*npoints; i++) {
1801 x = arrow_shapes[indx].points[i].x * len;
1802 y = arrow_shapes[indx].points[i].y * wd - offset;
1803 miny = min2(y, miny);
1804 maxy = max2(y, maxy);
1805 points[i].x = ROTX(x,y);
1806 points[i].y = ROTY(x,y);
1807 }
1808
1809 /* and the fill points array if there are fill points different from the outline */
1810 *nfillpoints = arrow_shapes[indx].numfillpts;
1811 for (i=0; i<*nfillpoints; i++) {
1812 x = arrow_shapes[indx].fillpoints[i].x * len;
1813 y = arrow_shapes[indx].fillpoints[i].y * wd - offset;
1814 miny = min2(y, miny);
1815 maxy = max2(y, maxy);
1816 fillpoints[i].x = ROTX(x,y);
1817 fillpoints[i].y = ROTY(x,y);
1818 }
1819
1820 /* to include thick lines in clip area */
1821 miny = min2(miny, -halfthick);
1822 maxy = max2(maxy, halfthick);
1823
1824 /* set clipping to the first three points of the arrowhead and
1825 the (enlarged) box surrounding it */
1826 *nclippts = 0;
1827 if (arrow_shapes[indx].clip) {
1828 for (i=0; i < 3; i++) {
1829 x = arrow_shapes[indx].points[i].x * len;
1830 y = arrow_shapes[indx].points[i].y * wd - offset;
1831 clippts[i].x = ROTX(x,y);
1832 clippts[i].y = ROTY(x,y);
1833 }
1834
1835 /* locate the tip of the head */
1836 tip = arrow_shapes[indx].tipno;
1837
1838 /* now make the box around it at least as large as the line thickness */
1839 /* start with last x, lower y */
1840
1841 clippts[i].x = ROTX(x,miny);
1842 clippts[i].y = ROTY(x,miny);
1843 i++;
1844 /* x tip, same y (note different offset in ROTX/Y2 rotation) */
1845 /* add halfthick in case the line cap style is Round or Projecting */
1846 clippts[i].x = ROTX2(arrow_shapes[indx].points[tip].x*len+halfthick + ZOOM_FACTOR, miny);
1847 clippts[i].y = ROTY2(arrow_shapes[indx].points[tip].x*len+halfthick + ZOOM_FACTOR, miny);
1848 i++;
1849 /* x tip, upper y (note different offset in ROTX/Y2 rotation) */
1850 /* add halfthick in case the line cap style is Round or Projecting */
1851 clippts[i].x = ROTX2(arrow_shapes[indx].points[tip].x*len+halfthick + ZOOM_FACTOR, maxy);
1852 clippts[i].y = ROTY2(arrow_shapes[indx].points[tip].x*len+halfthick + ZOOM_FACTOR, maxy);
1853 i++;
1854 /* first x of arrowhead, upper y */
1855 clippts[i].x = ROTX(arrow_shapes[indx].points[0].x*len, maxy);
1856 clippts[i].y = ROTY(arrow_shapes[indx].points[0].x*len, maxy);
1857 i++;
1858 }
1859 /* set the number of points in the clip or bounds */
1860 *nclippts = i;
1861 }
1862 }
1863
1864 /* draw the arrowhead resulting from the call to calc_arrow() */
1865 /* points[npoints] contains the outline and points2[npoints2] the points to be filled */
1866
draw_arrow(F_line * obj,F_arrow * arrow,zXPoint * points,int npoints,zXPoint * points2,int npoints2,int op)1867 void draw_arrow(F_line *obj, F_arrow *arrow, zXPoint *points, int npoints, zXPoint *points2, int npoints2, int op)
1868 {
1869 int fill;
1870
1871 if (obj->thickness == 0)
1872 return;
1873 if (arrow->type == 0 || arrow->type >= 13)
1874 fill = UNFILLED; /* old arrow head or new unfilled types */
1875 else if (arrow->style == 0)
1876 fill = NUMTINTPATS+NUMSHADEPATS-1; /* "hollow", fill with white */
1877 else
1878 fill = NUMSHADEPATS-1; /* "solid", fill with solid color */
1879 if (npoints2==0)
1880 /* no special fill, use outline points to fill too */
1881 pw_lines(canvas_win, points, npoints, op, obj->depth, round(arrow->thickness),
1882 SOLID_LINE, 0.0, JOIN_MITER, CAP_BUTT,
1883 fill, obj->pen_color, obj->pen_color);
1884 else {
1885 /* fill whole (outline) with white to obscure the line inside */
1886 pw_lines(canvas_win, points, npoints, op, obj->depth, 0,
1887 SOLID_LINE, 0.0, JOIN_MITER, CAP_BUTT,
1888 NUMTINTPATS+NUMSHADEPATS-1, obj->pen_color, obj->pen_color);
1889 /* draw outline */
1890 pw_lines(canvas_win, points, npoints, op, obj->depth, round(arrow->thickness),
1891 SOLID_LINE, 0.0, JOIN_MITER, CAP_BUTT,
1892 UNFILLED, obj->pen_color, obj->pen_color);
1893 /* fill special part with pen color */
1894 pw_lines(canvas_win, points2, npoints2, op, obj->depth, 0,
1895 SOLID_LINE, 0.0, JOIN_MITER, CAP_BUTT,
1896 NUMSHADEPATS-1, obj->pen_color, obj->pen_color);
1897 }
1898 }
1899
1900 /********************* CURVES FOR ARCS AND ELLIPSES ***************
1901
1902 This routine plot two dimensional curve defined by a second degree
1903 polynomial of the form : 2 2 f(x, y) = ax + by + g = 0
1904
1905 (x0,y0) is the starting point as well as ending point of the curve. The curve
1906 will translate with the offset xoff and yoff.
1907
1908 This algorithm is derived from the eight point algorithm in : "An Improved
1909 Algorithm for the generation of Nonparametric Curves" by Bernard W.
1910 Jordan, William J. Lennon and Barry D. Holm, IEEE Transaction on Computers
1911 Vol C-22, No. 12 December 1973.
1912
1913 This routine is only called for ellipses when the angle is 0 and the line type
1914 is not solid. For angles of 0 with solid lines, pw_curve() is called.
1915 For all other angles angle_ellipse() is called.
1916
1917 Will fill the curve if fill_style is != UNFILLED (-1)
1918 Call with draw_points = True to display the points using draw_point_array
1919 Otherwise global points array is filled with npoints values but
1920 not displayed.
1921 Call with draw_center = True and center_x, center_y set to draw endpoints
1922 to center point (xoff,yoff) (arc type 2, i.e. pie wedge)
1923
1924 ****************************************************************/
1925
1926 void
curve(Window window,int depth,int xstart,int ystart,int xend,int yend,Boolean draw_points,Boolean draw_center,int direction,int a,int b,int xoff,int yoff,int op,int thick,int style,float style_val,int fill_style,Color pen_color,Color fill_color,int cap_style)1927 curve(Window window, int depth, int xstart, int ystart, int xend, int yend,
1928 Boolean draw_points, Boolean draw_center, int direction,
1929 int a, int b, int xoff, int yoff, int op, int thick,
1930 int style, float style_val, int fill_style,
1931 Color pen_color, Color fill_color, int cap_style)
1932 {
1933 register int x, y;
1934 register double deltax, deltay, dfx, dfy;
1935 double dfxx, dfyy;
1936 double falpha, fx, fy, fxy, absfx, absfy, absfxy;
1937 int margin, test_succeed, inc, dec;
1938 float zoom;
1939
1940 /* if this depth is inactive, draw the curve in gray */
1941 if (depth < MAX_DEPTH+1 && !active_layer(depth)) {
1942 pen_color = MED_GRAY;
1943 fill_color = LT_GRAY;
1944 }
1945
1946 zoom = 1.0;
1947 /* if drawing on canvas (not in indicator button) adjust values by zoomscale */
1948 if (style != PANEL_LINE) {
1949 zoom = zoomscale;
1950 xstart = round(xstart * zoom);
1951 ystart = round(ystart * zoom);
1952 xend = round(xend * zoom);
1953 yend = round(yend * zoom);
1954 a = round(a * zoom);
1955 b = round(b * zoom);
1956 xoff = round(xoff * zoom);
1957 yoff = round(yoff * zoom);
1958 }
1959
1960 init_point_array();
1961
1962 /* this must be AFTER init_point_array() */
1963 if (a == 0 || b == 0)
1964 return;
1965
1966 x = xstart;
1967 y = ystart;
1968 dfx = 2 * (double) a * (double) xstart;
1969 dfy = 2 * (double) b * (double) ystart;
1970 dfxx = 2 * (double) a;
1971 dfyy = 2 * (double) b;
1972
1973 falpha = 0;
1974 if (direction) {
1975 inc = 1;
1976 dec = -1;
1977 } else {
1978 inc = -1;
1979 dec = 1;
1980 }
1981 if (xstart == xend && ystart == yend) {
1982 test_succeed = margin = 2;
1983 } else {
1984 test_succeed = margin = 3;
1985 }
1986
1987 if (!add_point(round((xoff + x)/zoom), round((yoff - y)/zoom))) {
1988 return;
1989 } else {
1990 while (test_succeed) {
1991 deltax = (dfy < 0) ? inc : dec;
1992 deltay = (dfx < 0) ? dec : inc;
1993 fx = falpha + dfx * deltax + a;
1994 fy = falpha + dfy * deltay + b;
1995 fxy = fx + fy - falpha;
1996 absfx = fabs(fx);
1997 absfy = fabs(fy);
1998 absfxy = fabs(fxy);
1999
2000 if ((absfxy <= absfx) && (absfxy <= absfy))
2001 falpha = fxy;
2002 else if (absfy <= absfx) {
2003 deltax = 0;
2004 falpha = fy;
2005 } else {
2006 deltay = 0;
2007 falpha = fx;
2008 }
2009 x += deltax;
2010 y += deltay;
2011 dfx += (dfxx * deltax);
2012 dfy += (dfyy * deltay);
2013
2014 if (!add_point(round((xoff + x)/zoom), round((yoff - y)/zoom))) {
2015 break;
2016 }
2017
2018 if ((abs(x - xend) < margin && abs(y - yend) < margin) &&
2019 (x != xend || y != yend))
2020 test_succeed--;
2021 }
2022
2023 }
2024
2025 if (xstart == xend && ystart == yend) /* end points should touch */
2026 if (!add_point(round((xoff + xstart)/zoom),
2027 round((yoff - ystart)/zoom)))
2028 too_many_points();
2029
2030 /* if this is arc type 2 then connect end points to center */
2031 if (draw_center) {
2032 if (!add_point(round(xoff/zoom),round(yoff/zoom)))
2033 too_many_points();
2034 if (!add_point(round((xoff + xstart)/zoom),round((yoff - ystart)/zoom)))
2035 too_many_points();
2036 }
2037
2038 if (draw_points) {
2039 draw_point_array(window, op, depth, thick, style, style_val, JOIN_BEVEL,
2040 cap_style, fill_style, pen_color, fill_color);
2041 }
2042 }
2043
2044 /* redraw all the picture objects */
2045
redraw_images(F_compound * obj)2046 void redraw_images(F_compound *obj)
2047 {
2048 F_line *l;
2049 F_compound *c;
2050
2051 for (c = obj->compounds; c != NULL; c = c->next) {
2052 redraw_images(c);
2053 }
2054 for (l = obj->lines; l != NULL; l = l->next) {
2055 if (l->type == T_PICTURE && l->pic->pic_cache && l->pic->pic_cache->numcols > 0)
2056 redisplay_line(l);
2057 }
2058 }
2059
too_many_points(void)2060 void too_many_points(void)
2061 {
2062 put_msg("Too many points, recompile with MAXNUMPTS > %d in w_drawprim.h", MAXNUMPTS);
2063 }
2064
debug_depth(int depth,int x,int y)2065 void debug_depth(int depth, int x, int y)
2066 {
2067 char str[10];
2068 PR_SIZE size;
2069
2070 if (appres.DEBUG) {
2071 sprintf(str,"%d",depth);
2072 size = textsize(roman_font, strlen(str), str);
2073 pw_text(canvas_win, x-size.length-round(3.0/zoomscale), round(y-3.0/zoomscale),
2074 PAINT, depth, roman_font, 0.0, str, RED, COLOR_NONE);
2075 }
2076 }
2077
2078 /*********************** SPLINE ***************************/
2079
2080 /**********************************/
2081 /* include common spline routines */
2082 /**********************************/
2083
2084 void
draw_spline(F_spline * spline,int op)2085 draw_spline(F_spline *spline, int op)
2086 {
2087 Boolean success;
2088 int xmin, ymin, xmax, ymax;
2089 int i;
2090 F_point *p;
2091 float precision;
2092
2093 spline_bound(spline, &xmin, &ymin, &xmax, &ymax);
2094 if (!overlapping(ZOOMX(xmin), ZOOMY(ymin), ZOOMX(xmax), ZOOMY(ymax),
2095 clip_xmin, clip_ymin, clip_xmax, clip_ymax))
2096 return;
2097
2098 precision = (display_zoomscale < ZOOM_PRECISION) ? LOW_PRECISION
2099 : HIGH_PRECISION;
2100
2101 if (appres.shownums && active_layer(spline->depth)) {
2102 for (i=0, p=spline->points; p; p=p->next) {
2103 /* label the point number above the point */
2104 sprintf(bufx,"%d",i++);
2105 pw_text(canvas_win, p->x, round(p->y-3.0/zoomscale), PAINT, spline->depth,
2106 roman_font, 0.0, bufx, RED, COLOR_NONE);
2107 }
2108 }
2109 if (open_spline(spline))
2110 success = compute_open_spline(spline, precision);
2111 else
2112 success = compute_closed_spline(spline, precision);
2113 if (success) {
2114 /* setup clipping so that spline doesn't protrude beyond arrowhead */
2115 /* also create the arrowheads */
2116 clip_arrows((F_line *)spline,O_SPLINE,op,4);
2117
2118 draw_point_array(canvas_win, op, spline->depth, spline->thickness,
2119 spline->style, spline->style_val,
2120 JOIN_MITER, spline->cap_style,
2121 spline->fill_style, spline->pen_color,
2122 spline->fill_color);
2123 /* restore clipping */
2124 set_clip_window(clip_xmin, clip_ymin, clip_xmax, clip_ymax);
2125
2126 if (spline->for_arrow) /* forward arrow */
2127 draw_arrow((F_line *)spline, spline->for_arrow, farpts, nfpts, farfillpts, nffillpts, op);
2128 if (spline->back_arrow) /* backward arrow */
2129 draw_arrow((F_line *)spline, spline->back_arrow, barpts, nbpts, barfillpts, nbfillpts, op);
2130 /* write the depth on the object */
2131 debug_depth(spline->depth,spline->points->x,spline->points->y);
2132 }
2133 }
2134
2135 void
quick_draw_spline(F_spline * spline,int operator)2136 quick_draw_spline(F_spline *spline, int operator)
2137 {
2138 int k;
2139 float step;
2140 F_point *p0, *p1, *p2, *p3;
2141 F_sfactor *s0, *s1, *s2, *s3;
2142
2143 init_point_array();
2144
2145 INIT_CONTROL_POINTS(spline, p0, s0, p1, s1, p2, s2, p3, s3);
2146
2147 for (k=0 ; p3!=NULL ; k++) {
2148 SPLINE_SEGMENT_LOOP(k, p0, p1, p2, p3, s1->s, s2->s, LOW_PRECISION);
2149 NEXT_CONTROL_POINTS(p0, s0, p1, s1, p2, s2, p3, s3);
2150 }
2151 draw_point_array(canvas_win, operator, spline->depth, spline->thickness,
2152 spline->style, spline->style_val,
2153 JOIN_MITER, spline->cap_style,
2154 spline->fill_style, spline->pen_color, spline->fill_color);
2155 }
2156
2157