1 /*
2  *
3 Copyright (c) 1991  X Consortium
4 
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 Except as contained in this notice, the name of the X Consortium shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from the X Consortium.
25  *
26  */
27 
28 /*
29  * draw.c
30  *
31  * accept dvi function calls and translate to X
32  */
33 
34 /*
35   Support for ditroff drawing commands added: lines, circles, ellipses,
36   arcs and splines.  Splines are approximated as short lines by iterating
37   a simple approximation algorithm.  This seems good enough for previewing.
38 
39   David Evans <dre@cs.nott.ac.uk>, 14th March, 1990
40 */
41 
42 #include <X11/Xos.h>
43 #include <X11/IntrinsicP.h>
44 #include <X11/StringDefs.h>
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <math.h>
48 #include "DviP.h"
49 #include <stdlib.h>
50 
51 #ifndef M_PI
52 #define M_PI 3.14159265358979323846264338327950
53 #endif
54 
55 /*	the following are for use in the spline approximation algorithm */
56 
57 typedef struct	Point {
58     double	x;
59     double	y;
60     struct Point	*next;
61 } Point;
62 
63 #define	ITERATIONS	10	/* iterations to approximate spline */
64 
65 #define	midx(p,q)	((p->x + q->x) / 2)	/* mid x point on pq */
66 #define	midy(p,q)	((p->y + q->y) / 2)	/* mid y point on pq */
67 
68 #define	length(p,q)	sqrt(((q->x - p->x)*(q->x - p->x)) \
69 			     + ((q->y - p->y)*(q->y - p->y))) /* length of pq */
70 
71 static Point *spline = (Point *)NULL;	/* head of spline linked list */
72 
73 static void	ApproxSpline(int n);
74 static void	DeletePoint(Point *p);
75 static void	DrawSplineSegments(DviWidget dw);
76 static int	GetSpline(const char *s);
77 static void	InsertPoint(Point *p, Point *q);
78 static void	LineApprox(Point *p1, Point *p2, Point *p3);
79 static Point *	MakePoint(double x, double y);
80 
81 void
HorizontalMove(DviWidget dw,int delta)82 HorizontalMove(DviWidget dw, int delta)
83 {
84     dw->dvi.state->x += delta;
85 }
86 
87 void
HorizontalGoto(DviWidget dw,int NewPosition)88 HorizontalGoto(DviWidget dw, int NewPosition)
89 {
90     dw->dvi.state->x = NewPosition;
91 }
92 
93 void
VerticalMove(DviWidget dw,int delta)94 VerticalMove(DviWidget dw, int delta)
95 {
96     dw->dvi.state->y += delta;
97 }
98 
99 void
VerticalGoto(DviWidget dw,int NewPosition)100 VerticalGoto(DviWidget dw, int NewPosition)
101 {
102     dw->dvi.state->y = NewPosition;
103 }
104 
105 #ifdef USE_XFT
106 static void
DrawText(DviWidget dw)107 DrawText (DviWidget dw)
108 {
109     int	    i;
110     XftFont *font;
111 
112     font = dw->dvi.cache.font;
113     for (i = 0; i <= dw->dvi.cache.index; i++)
114     {
115 	if (dw->dvi.cache.cache[i].font)
116 	    font = dw->dvi.cache.cache[i].font;
117 	XftDrawString8 (dw->dvi.draw, &dw->dvi.black,
118 			font,
119 			dw->dvi.cache.cache[i].x,
120 			dw->dvi.cache.start_y,
121 			(unsigned char *) dw->dvi.cache.cache[i].chars,
122 			dw->dvi.cache.cache[i].nchars);
123     }
124 }
125 #endif
126 
127 void
FlushCharCache(DviWidget dw)128 FlushCharCache (DviWidget dw)
129 {
130     int	    xx, yx;
131 
132     xx = ToX(dw, dw->dvi.state->x);
133     yx = ToX(dw, dw->dvi.state->y);
134     if (dw->dvi.cache.char_index != 0)
135     {
136 #ifdef USE_XFT
137 	DrawText (dw);
138 #else
139 	XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
140 		    dw->dvi.cache.start_x, dw->dvi.cache.start_y,
141 		    dw->dvi.cache.cache, dw->dvi.cache.index + 1);
142 #endif
143     }
144     dw->dvi.cache.index = 0;
145     dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
146     if (dw->dvi.noPolyText)
147 	dw->dvi.cache.max = 1;
148     dw->dvi.cache.char_index = 0;
149     dw->dvi.cache.cache[0].nchars = 0;
150     dw->dvi.cache.start_x = dw->dvi.cache.x = xx;
151     dw->dvi.cache.start_y = dw->dvi.cache.y = yx;
152 }
153 
154 void
SetGCForDraw(DviWidget dw)155 SetGCForDraw (DviWidget dw)
156 {
157     int	lw;
158     if (dw->dvi.state->line_style != dw->dvi.line_style ||
159 	dw->dvi.state->line_width != dw->dvi.line_width)
160     {
161 	lw = ToX(dw, dw->dvi.state->line_width);
162 	if (lw <= 1)
163 	    lw = 0;
164 	XSetLineAttributes (XtDisplay (dw), dw->dvi.normal_GC,
165 			    lw, LineSolid, CapButt, JoinMiter);
166 	dw->dvi.line_style = dw->dvi.state->line_style;
167 	dw->dvi.line_width = dw->dvi.state->line_width;
168     }
169 }
170 
171 void
DrawLine(DviWidget dw,int x,int y)172 DrawLine (DviWidget dw, int x, int y)
173 {
174     if (dw->dvi.display_enable)
175 	XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
176 		   ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y),
177 		   ToX(dw, dw->dvi.state->x + x), ToX(dw,dw->dvi.state->y + y));
178     dw->dvi.state->x += x;
179     dw->dvi.state->y += y;
180 }
181 
182 void
DrawCircle(DviWidget dw,int diameter)183 DrawCircle (DviWidget dw, int diameter)
184 {
185     if (dw->dvi.display_enable)
186     	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
187 	      	  ToX(dw, dw->dvi.state->x),
188 	      	  ToX(dw, dw->dvi.state->y - (diameter / 2)),
189 	      	  ToX(dw, diameter), ToX(dw, diameter), 0, 360 * 64);
190     dw->dvi.state->x += diameter;
191 }
192 
193 void
DrawEllipse(DviWidget dw,int a,int b)194 DrawEllipse (DviWidget dw, int a, int b)
195 {
196     if (dw->dvi.display_enable)
197 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
198 		  ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y - (b / 2)),
199 		  ToX(dw,a), ToX(dw,b), 0, 360 * 64);
200     dw->dvi.state->x += a;
201 }
202 
203 
204 /*	Convert angle in degrees to 64ths of a degree */
205 
206 static int
ConvertAngle(int theta)207 ConvertAngle(int theta)
208 {
209     return(theta * 64);
210 }
211 
212 void
DrawArc(DviWidget dw,int x0,int y0,int x1,int y1)213 DrawArc (DviWidget dw, int x0, int y0, int x1, int y1)
214 {
215     int	xc, yc, x2, y2, r;
216     int	angle1, angle2;
217 
218     /* centre */
219     xc = dw->dvi.state->x + x0;
220     yc = dw->dvi.state->y + y0;
221 
222     /* to */
223     x2 = xc + x1;
224     y2 = yc + y1;
225 
226     dw->dvi.state->x = x2;
227     dw->dvi.state->y = y2;
228 
229     if (dw->dvi.display_enable) {
230 
231 	/* radius */
232 	r = (int)sqrt((float) x1 * x1 + (float) y1 * y1);
233 
234 	/* start and finish angles */
235 	if (x0 == 0) {
236 		if (y0 >= 0)
237 			angle1 = 90;
238 		else
239 			angle1 = 270;
240 	}
241 	else {
242 		angle1 = (int) (atan((double)(y0) / (double)(x0)) * 180 / M_PI);
243 		if (x0 > 0)
244 			angle1 = 180 - angle1;
245 		else
246 			angle1 = -angle1;
247 	}
248 
249 	if (x1 == 0) {
250 		if (y1 <= 0)
251 			angle2 = 90;
252 		else
253 			angle2 = 270;
254 	}
255 	else {
256 		angle2 = (int) (atan((double)(y1) / (double)(x1)) * 180 / M_PI);
257 		if (x1 < 0)
258 			angle2 = 180 - angle2;
259 		else
260 			angle2 = -angle2;
261 	}
262 
263 	if (angle1 < 0)
264 		angle1 += 360;
265 	if (angle2 < 0)
266 		angle2 += 360;
267 
268 	if (angle2 < angle1)
269 		angle1 -= 360;
270 	angle2 = angle2 - angle1;
271 
272 	angle1 = ConvertAngle(angle1);
273 	angle2 = ConvertAngle(angle2);
274 
275 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
276 		  ToX(dw, xc - r), ToX(dw, yc - r),
277 		  ToX(dw, 2 * r), ToX(dw, 2 * r),
278 		  angle1, angle2);
279     }
280 }
281 
282 /* copy next non-blank string from p to temp, update p */
283 
284 static const char *
getstr(const char * p,char * temp)285 getstr(const char *p, char *temp)
286 {
287     while (*p == ' ' || *p == '\t' || *p == '\n')
288 	p++;
289     if (*p == '\0') {
290 	temp[0] = 0;
291 	return((char *)NULL);
292     }
293     while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
294 	*temp++ = *p++;
295     *temp = '\0';
296     return(p);
297 }
298 
299 
300 /*	Draw a spline by approximating with short lines.      */
301 
302 /*ARGSUSED*/
303 void
DrawSpline(DviWidget dw,const char * s,int len)304 DrawSpline (DviWidget dw, const char *s, int len)
305 {
306     int		n;
307 
308     /* get coordinate pairs into spline linked list */
309     if ((n = GetSpline(s)) <= 0)
310 	return;
311 
312     ApproxSpline(n);
313 
314     DrawSplineSegments(dw);
315 }
316 
317 
318 /*	Parse string s to create a linked list of Point's with spline */
319 /*	as its head.  Return the number of coordinate pairs found.    */
320 
321 static int
GetSpline(const char * s)322 GetSpline(const char *s)
323 {
324     double	x, y, x1, y1;
325     int		n = 0;
326     Point	*pt;
327     const char	*p = s;
328     char        d[10];
329 
330     if (!*p)
331 	return(n);
332 
333     pt = spline = MakePoint(0.0, 0.0);
334     n = 1;
335     x = y = 0.0;
336     p = s;
337     while (p && *p) {
338 	if ((p = getstr(p, d)) == (const char *)NULL)
339 	    break;
340 	x1 = x + atof(d);
341 	if ((p = getstr(p, d)) == (const char *)NULL)
342 	    break;
343 	y1 = y + atof(d);
344 	pt->next = MakePoint(x1, y1);
345 	pt = pt->next;
346 	x = pt->x;
347 	y = pt->y;
348 	n++;
349     }
350 
351     /* number of pairs of points */
352 
353     return(n);
354 }
355 
356 /*	Approximate a spline by lines generated by iterations of the	  */
357 /*	approximation algorithm from the original n points in the spline. */
358 
359 static void
ApproxSpline(int n)360 ApproxSpline(int n)
361 {
362     int		mid, j;
363     Point	*p1, *p2, *p3, *p;
364 
365     if (n < 3)
366 	return;
367 
368     /* number of mid-points to calculate */
369     mid = n - 3;
370 
371     /* remember original points are stored as an array of n points */
372     /* so I can index it directly to calculate mid-points only.	   */
373     if (mid > 0) {
374 	p = spline->next;
375 	j = 1;
376 	while (j < n-2) {
377 	    p1 = p;
378 	    p = p->next;
379 	    p2 = p;
380 	    InsertPoint(p1, MakePoint(midx(p1, p2), midy(p1, p2)));
381 	    j++;
382 	}
383     }
384 
385     /* Now approximate curve by line segments.		   */
386     /* There *should* be the correct number of points now! */
387 
388     p = spline;
389     while (p != (Point *)NULL) {
390 	p1 = p;
391 	if ((p = p->next) == (Point *)NULL)
392 	    break;
393 	p2 = p;
394 	if ((p = p->next) == (Point *)NULL)
395 	    break;
396 	p3 = p;		/* This point becomes first point of next curve */
397 
398 	LineApprox(p1, p2, p3);
399     }
400 }
401 
402 
403 /*	p1, p2, and p3 are initially 3 *consecutive* points on the curve. */
404 /*	For each adjacent pair of points find the mid-point, insert this  */
405 /*	in the linked list, delete the first of the two used (unless it   */
406 /*	is the first for this curve).  Repeat this ITERATIONS times.	  */
407 
408 /*ARGSUSED*/
409 static void
LineApprox(Point * p1,Point * p2,Point * p3)410 LineApprox(Point *p1, Point *p2, Point *p3)
411 {
412     Point	*p4, *p;
413     int		reps = ITERATIONS;
414 
415     while (reps) {
416 	for (p = p1; p != (Point *)NULL && p != p3; ) {
417 	    InsertPoint(p, p4 = MakePoint( midx(p,p->next), midy(p,p->next) ));
418 	    if (p != p1)
419 		DeletePoint(p);
420 	    p = p4->next;		/* skip inserted point! */
421 	}
422 	reps--;
423     }
424 }
425 
426 
427 /*	Traverse the linked list, calling DrawLine to approximate the */
428 /*	spline curve.  Rounding errors are taken into account so that */
429 /*	the "curve" is continuous, and ends up where expected.	      */
430 
431 static void
DrawSplineSegments(DviWidget dw)432 DrawSplineSegments(DviWidget dw)
433 {
434     Point	*p, *q;
435     double	x1, y1;
436     int		dx, dy;
437     double	xpos, ypos;
438 
439     p = spline;
440     dx = dy = 0;
441 
442     /* save the start position */
443 
444     xpos = dw->dvi.state->x;
445     ypos = dw->dvi.state->y;
446 
447     x1 = y1 = 0.0;
448 
449     while (p != (Point *)NULL) {
450 	dx = p->x - x1 + 0.5;
451 	dy = p->y - y1 + 0.5;
452 	DrawLine (dw, dx, dy);
453 
454 	x1 = p->x;
455 	y1 = p->y;
456 	dw->dvi.state->x = xpos + x1;
457 	dw->dvi.state->y = ypos + y1;
458 
459 	q = p;
460 	p = p->next;
461 	XtFree((char *)q);
462     }
463     spline = (Point *)NULL;
464 }
465 
466 
467 /*	Malloc memory for a Point, and initialise the elements to x, y, NULL */
468 /*	Return a pointer to the new Point.				     */
469 
470 static Point *
MakePoint(double x,double y)471 MakePoint(double x, double y)
472 {
473     Point	*p;
474 
475     p = (Point *) XtMalloc (sizeof (Point));
476     p->x = x;
477     p->y = y;
478     p->next = (Point *)NULL;
479 
480     return(p);
481 }
482 
483 
484 /*	Insert point q in linked list after point p. */
485 
486 static void
InsertPoint(Point * p,Point * q)487 InsertPoint(Point *p, Point *q)
488 {
489     /* point q to the next point */
490     q->next = p->next;
491 
492     /* point p to new inserted one */
493     p->next = q;
494 }
495 
496 /*	Delete point p from the linked list. */
497 
498 static void
DeletePoint(Point * p)499 DeletePoint(Point *p)
500 {
501     Point	*tmp;
502 
503     tmp = p->next;
504     p->x = p->next->x;
505     p->y = p->next->y;
506     p->next = p->next->next;
507     XtFree((char *)tmp);
508 }
509