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