draw.c revision c166fba9
1/*
2 *
3Copyright (c) 1991  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of the X Consortium shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in 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
57typedef 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
71static Point *spline = (Point *)NULL;	/* head of spline linked list */
72
73static void	ApproxSpline(int n);
74static void	DeletePoint(Point *p);
75static void	DrawSplineSegments(DviWidget dw);
76static int	GetSpline(const char *s);
77static void	InsertPoint(Point *p, Point *q);
78static void	LineApprox(Point *p1, Point *p2, Point *p3);
79static Point *	MakePoint(double x, double y);
80
81void
82HorizontalMove(DviWidget dw, int delta)
83{
84    dw->dvi.state->x += delta;
85}
86
87void
88HorizontalGoto(DviWidget dw, int NewPosition)
89{
90    dw->dvi.state->x = NewPosition;
91}
92
93void
94VerticalMove(DviWidget dw, int delta)
95{
96    dw->dvi.state->y += delta;
97}
98
99void
100VerticalGoto(DviWidget dw, int NewPosition)
101{
102    dw->dvi.state->y = NewPosition;
103}
104
105#ifdef USE_XFT
106static void
107DrawText (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
127void
128FlushCharCache (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
154void
155SetGCForDraw (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
171void
172DrawLine (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
182void
183DrawCircle (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
193void
194DrawEllipse (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
206static int
207ConvertAngle(int theta)
208{
209    return(theta * 64);
210}
211
212void
213DrawArc (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
284static const char *
285getstr(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*/
303void
304DrawSpline (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
321static int
322GetSpline(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
359static void
360ApproxSpline(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*/
409static void
410LineApprox(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
431static void
432DrawSplineSegments(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
470static Point *
471MakePoint(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
486static void
487InsertPoint(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
498static void
499DeletePoint(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