draw.c revision f80a6dcd
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
73Point	*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(dw, delta)
85	DviWidget	dw;
86	int		delta;
87{
88    dw->dvi.state->x += delta;
89}
90
91void
92HorizontalGoto(dw, NewPosition)
93	DviWidget	dw;
94	int		NewPosition;
95{
96    dw->dvi.state->x = NewPosition;
97}
98
99void
100VerticalMove(dw, delta)
101	DviWidget	dw;
102	int		delta;
103{
104    dw->dvi.state->y += delta;
105}
106
107void
108VerticalGoto(dw, NewPosition)
109	DviWidget	dw;
110	int		NewPosition;
111{
112    dw->dvi.state->y = NewPosition;
113}
114
115#ifdef USE_XFT
116static void
117DrawText (DviWidget dw)
118{
119    int	    i;
120    XftFont *font;
121
122    font = dw->dvi.cache.font;
123    for (i = 0; i <= dw->dvi.cache.index; i++)
124    {
125	if (dw->dvi.cache.cache[i].font)
126	    font = dw->dvi.cache.cache[i].font;
127	XftDrawString8 (dw->dvi.draw, &dw->dvi.black,
128			font,
129			dw->dvi.cache.cache[i].x,
130			dw->dvi.cache.start_y,
131			(unsigned char *) dw->dvi.cache.cache[i].chars,
132			dw->dvi.cache.cache[i].nchars);
133    }
134}
135#endif
136
137void
138FlushCharCache (dw)
139	DviWidget	dw;
140{
141    int	    xx, yx;
142
143    xx = ToX(dw, dw->dvi.state->x);
144    yx = ToX(dw, dw->dvi.state->y);
145    if (dw->dvi.cache.char_index != 0)
146    {
147#ifdef USE_XFT
148	DrawText (dw);
149#else
150	XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
151		    dw->dvi.cache.start_x, dw->dvi.cache.start_y,
152		    dw->dvi.cache.cache, dw->dvi.cache.index + 1);
153#endif
154    }
155    dw->dvi.cache.index = 0;
156    dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
157    if (dw->dvi.noPolyText)
158	dw->dvi.cache.max = 1;
159    dw->dvi.cache.char_index = 0;
160    dw->dvi.cache.cache[0].nchars = 0;
161    dw->dvi.cache.start_x = dw->dvi.cache.x = xx;
162    dw->dvi.cache.start_y = dw->dvi.cache.y = yx;
163}
164
165#if 0
166void
167ClearPage (DviWidget dw)
168{
169    if (dw->dvi.display_enable)
170	XClearWindow (XtDisplay (dw), XtWindow (dw));
171}
172#endif
173
174void
175SetGCForDraw (dw)
176    DviWidget	dw;
177{
178    int	lw;
179    if (dw->dvi.state->line_style != dw->dvi.line_style ||
180	dw->dvi.state->line_width != dw->dvi.line_width)
181    {
182	lw = ToX(dw, dw->dvi.state->line_width);
183	if (lw <= 1)
184	    lw = 0;
185	XSetLineAttributes (XtDisplay (dw), dw->dvi.normal_GC,
186			    lw, LineSolid, CapButt, JoinMiter);
187	dw->dvi.line_style = dw->dvi.state->line_style;
188	dw->dvi.line_width = dw->dvi.state->line_width;
189    }
190}
191
192void
193DrawLine (dw, x, y)
194    DviWidget	dw;
195    int		x, y;
196{
197    if (dw->dvi.display_enable)
198	XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
199		   ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y),
200		   ToX(dw, dw->dvi.state->x + x), ToX(dw,dw->dvi.state->y + y));
201    dw->dvi.state->x += x;
202    dw->dvi.state->y += y;
203}
204
205void
206DrawCircle (dw, diameter)
207	DviWidget	dw;
208	int		diameter;
209{
210    if (dw->dvi.display_enable)
211    	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
212	      	  ToX(dw, dw->dvi.state->x),
213	      	  ToX(dw, dw->dvi.state->y - (diameter / 2)),
214	      	  ToX(dw, diameter), ToX(dw, diameter), 0, 360 * 64);
215    dw->dvi.state->x += diameter;
216}
217
218void
219DrawEllipse (dw, a, b)
220	DviWidget	dw;
221	int		a, b;
222{
223    if (dw->dvi.display_enable)
224	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
225		  ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y - (b / 2)),
226		  ToX(dw,a), ToX(dw,b), 0, 360 * 64);
227    dw->dvi.state->x += a;
228}
229
230
231/*	Convert angle in degrees to 64ths of a degree */
232
233static int
234ConvertAngle(int theta)
235{
236    return(theta * 64);
237}
238
239void
240DrawArc (dw, x0, y0, x1, y1)
241	DviWidget	dw;
242	int		x0, y0, x1, y1;
243{
244    int	xc, yc, x2, y2, r;
245    int	angle1, angle2;
246
247    /* centre */
248    xc = dw->dvi.state->x + x0;
249    yc = dw->dvi.state->y + y0;
250
251    /* to */
252    x2 = xc + x1;
253    y2 = yc + y1;
254
255    dw->dvi.state->x = x2;
256    dw->dvi.state->y = y2;
257
258    if (dw->dvi.display_enable) {
259
260	/* radius */
261	r = (int)sqrt((float) x1 * x1 + (float) y1 * y1);
262
263	/* start and finish angles */
264	if (x0 == 0) {
265		if (y0 >= 0)
266			angle1 = 90;
267		else
268			angle1 = 270;
269	}
270	else {
271		angle1 = (int) (atan((double)(y0) / (double)(x0)) * 180 / M_PI);
272		if (x0 > 0)
273			angle1 = 180 - angle1;
274		else
275			angle1 = -angle1;
276	}
277
278	if (x1 == 0) {
279		if (y1 <= 0)
280			angle2 = 90;
281		else
282			angle2 = 270;
283	}
284	else {
285		angle2 = (int) (atan((double)(y1) / (double)(x1)) * 180 / M_PI);
286		if (x1 < 0)
287			angle2 = 180 - angle2;
288		else
289			angle2 = -angle2;
290	}
291
292	if (angle1 < 0)
293		angle1 += 360;
294	if (angle2 < 0)
295		angle2 += 360;
296
297	if (angle2 < angle1)
298		angle1 -= 360;
299	angle2 = angle2 - angle1;
300
301	angle1 = ConvertAngle(angle1);
302	angle2 = ConvertAngle(angle2);
303
304	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
305		  ToX(dw, xc - r), ToX(dw, yc - r),
306		  ToX(dw, 2 * r), ToX(dw, 2 * r),
307		  angle1, angle2);
308    }
309}
310
311/* copy next non-blank string from p to temp, update p */
312
313static char *
314getstr(char *p, char *temp)
315{
316    while (*p == ' ' || *p == '\t' || *p == '\n')
317	p++;
318    if (*p == '\0') {
319	temp[0] = 0;
320	return((char *)NULL);
321    }
322    while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
323	*temp++ = *p++;
324    *temp = '\0';
325    return(p);
326}
327
328
329/*	Draw a spline by approximating with short lines.      */
330
331/*ARGSUSED*/
332void
333DrawSpline (dw, s, len)
334	DviWidget	dw;
335	char		*s;
336	int		len;
337{
338    int		n;
339
340    /* get coordinate pairs into spline linked list */
341    if ((n = GetSpline(s)) <= 0)
342	return;
343
344    ApproxSpline(n);
345
346    DrawSplineSegments(dw);
347}
348
349
350/*	Parse string s to create a linked list of Point's with spline */
351/*	as its head.  Return the number of coordinate pairs found.    */
352
353static int
354GetSpline(s)
355    char *s;
356{
357    double	x, y, x1, y1;
358    int		n = 0;
359    Point	*pt;
360    char	*p = s, d[10];
361
362    if (!*p)
363	return(n);
364
365    pt = spline = MakePoint(0.0, 0.0);
366    n = 1;
367    x = y = 0.0;
368    p = s;
369    while (p && *p) {
370	if ((p = getstr(p, d)) == (char *)NULL)
371	    break;
372	x1 = x + atof(d);
373	if ((p = getstr(p, d)) == (char *)NULL)
374	    break;
375	y1 = y + atof(d);
376	pt->next = MakePoint(x1, y1);
377	pt = pt->next;
378	x = pt->x;
379	y = pt->y;
380	n++;
381    }
382
383    /* number of pairs of points */
384
385    return(n);
386}
387
388/*	Approximate a spline by lines generated by iterations of the	  */
389/*	approximation algorithm from the original n points in the spline. */
390
391static void
392ApproxSpline(n)
393int n;
394{
395    int		mid, j;
396    Point	*p1, *p2, *p3, *p;
397
398    if (n < 3)
399	return;
400
401    /* number of mid-points to calculate */
402    mid = n - 3;
403
404    /* remember original points are stored as an array of n points */
405    /* so I can index it directly to calculate mid-points only.	   */
406    if (mid > 0) {
407	p = spline->next;
408	j = 1;
409	while (j < n-2) {
410	    p1 = p;
411	    p = p->next;
412	    p2 = p;
413	    InsertPoint(p1, MakePoint(midx(p1, p2), midy(p1, p2)));
414	    j++;
415	}
416    }
417
418    /* Now approximate curve by line segments.		   */
419    /* There *should* be the correct number of points now! */
420
421    p = spline;
422    while (p != (Point *)NULL) {
423	p1 = p;
424	if ((p = p->next) == (Point *)NULL)
425	    break;
426	p2 = p;
427	if ((p = p->next) == (Point *)NULL)
428	    break;
429	p3 = p;		/* This point becomes first point of next curve */
430
431	LineApprox(p1, p2, p3);
432    }
433}
434
435
436/*	p1, p2, and p3 are initially 3 *consecutive* points on the curve. */
437/*	For each adjacent pair of points find the mid-point, insert this  */
438/*	in the linked list, delete the first of the two used (unless it   */
439/*	is the first for this curve).  Repeat this ITERATIONS times.	  */
440
441/*ARGSUSED*/
442static void
443LineApprox(p1, p2, p3)
444Point	*p1, *p2, *p3;
445{
446    Point	*p4, *p;
447    int		reps = ITERATIONS;
448
449    while (reps) {
450	for (p = p1; p != (Point *)NULL && p != p3; ) {
451	    InsertPoint(p, p4 = MakePoint( midx(p,p->next), midy(p,p->next) ));
452	    if (p != p1)
453		DeletePoint(p);
454	    p = p4->next;		/* skip inserted point! */
455	}
456	reps--;
457    }
458}
459
460
461/*	Traverse the linked list, calling DrawLine to approximate the */
462/*	spline curve.  Rounding errors are taken into account so that */
463/*	the "curve" is continuous, and ends up where expected.	      */
464
465static void
466DrawSplineSegments(dw)
467DviWidget dw;
468{
469    Point	*p, *q;
470    double	x1, y1;
471    int		dx, dy;
472    double	xpos, ypos;
473
474    p = spline;
475    dx = dy = 0;
476
477    /* save the start position */
478
479    xpos = dw->dvi.state->x;
480    ypos = dw->dvi.state->y;
481
482    x1 = y1 = 0.0;
483
484    while (p != (Point *)NULL) {
485	dx = p->x - x1 + 0.5;
486	dy = p->y - y1 + 0.5;
487	DrawLine (dw, dx, dy);
488
489	x1 = p->x;
490	y1 = p->y;
491	dw->dvi.state->x = xpos + x1;
492	dw->dvi.state->y = ypos + y1;
493
494	q = p;
495	p = p->next;
496	XtFree((char *)q);
497    }
498    spline = (Point *)NULL;
499}
500
501
502/*	Malloc memory for a Point, and initialise the elements to x, y, NULL */
503/*	Return a pointer to the new Point.				     */
504
505static Point *
506MakePoint(x, y)
507double x, y;
508{
509    Point	*p;
510
511    p = (Point *) XtMalloc (sizeof (Point));
512    p->x = x;
513    p->y = y;
514    p->next = (Point *)NULL;
515
516    return(p);
517}
518
519
520/*	Insert point q in linked list after point p. */
521
522static void
523InsertPoint(p, q)
524Point *p, *q;
525{
526    /* point q to the next point */
527    q->next = p->next;
528
529    /* point p to new inserted one */
530    p->next = q;
531}
532
533/*	Delete point p from the linked list. */
534
535static void
536DeletePoint(p)
537Point	*p;
538{
539    Point	*tmp;
540
541    tmp = p->next;
542    p->x = p->next->x;
543    p->y = p->next->y;
544    p->next = p->next->next;
545    XtFree((char *)tmp);
546}
547