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