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