1 /*
2  * draw.c
3  *
4  * accept dvi function calls and translate to X
5  */
6 
7 #include <X11/Xos.h>
8 #include <X11/IntrinsicP.h>
9 #include <X11/StringDefs.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <math.h>
13 
14 /* math.h on a Sequent doesn't define M_PI, apparently */
15 #ifndef M_PI
16 #define M_PI	3.14159265358979323846
17 #endif
18 
19 #include "DviP.h"
20 
21 #define DeviceToX(dw, n) ((int)((n) * (dw)->dvi.scale_factor + .5))
22 #define XPos(dw) (DeviceToX((dw), (dw)->dvi.state->x - \
23                   (dw)->dvi.text_device_width) + (dw)->dvi.text_x_width)
24 #define YPos(dw) (DeviceToX((dw), (dw)->dvi.state->y))
25 
26 static int FakeCharacter(DviWidget, char *, int);
27 
28 /* font.c */
29 extern int MaxFontPosition(DviWidget);
30 
31 void
HorizontalMove(DviWidget dw,int delta)32 HorizontalMove(DviWidget dw, int delta)
33 {
34 	dw->dvi.state->x += delta;
35 }
36 
37 void
HorizontalGoto(DviWidget dw,int NewPosition)38 HorizontalGoto(DviWidget dw, int NewPosition)
39 {
40 	dw->dvi.state->x = NewPosition;
41 }
42 
43 void
VerticalMove(DviWidget dw,int delta)44 VerticalMove(DviWidget dw, int delta)
45 {
46 	dw->dvi.state->y += delta;
47 }
48 
49 void
VerticalGoto(DviWidget dw,int NewPosition)50 VerticalGoto(DviWidget dw, int NewPosition)
51 {
52 	dw->dvi.state->y = NewPosition;
53 }
54 
55 void
AdjustCacheDeltas(DviWidget dw)56 AdjustCacheDeltas (DviWidget dw)
57 {
58 	int extra;
59 	int nadj;
60 	int i;
61 
62 	nadj = 0;
63 	extra = DeviceToX(dw, dw->dvi.text_device_width)
64 		- dw->dvi.text_x_width;
65 	if (extra == 0)
66 		return;
67 	for (i = 0; i <= dw->dvi.cache.index; i++)
68 		if (dw->dvi.cache.adjustable[i])
69 			++nadj;
70 	dw->dvi.text_x_width += extra;
71 	if (nadj <= 1)
72 		return;
73 	for (i = 0; i <= dw->dvi.cache.index; i++)
74 		if (dw->dvi.cache.adjustable[i]) {
75 			int x;
76 			int *deltap;
77 
78 			x = extra/nadj;
79 			deltap = &dw->dvi.cache.cache[i].delta;
80 #define MIN_DELTA 2
81 			if (*deltap > 0 && x + *deltap < MIN_DELTA) {
82 				x = MIN_DELTA - *deltap;
83 				if (x <= 0)
84 					*deltap = MIN_DELTA;
85 				else
86 					x = 0;
87 			}
88 			else
89 				*deltap += x;
90 			extra -= x;
91 			--nadj;
92 			dw->dvi.cache.adjustable[i] = 0;
93 		}
94 }
95 
96 void
FlushCharCache(DviWidget dw)97 FlushCharCache (DviWidget dw)
98 {
99 	if (dw->dvi.cache.char_index != 0) {
100 		AdjustCacheDeltas (dw);
101 		XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
102 			   dw->dvi.cache.start_x, dw->dvi.cache.start_y,
103 			   dw->dvi.cache.cache, dw->dvi.cache.index + 1);
104 	}
105 	dw->dvi.cache.index = 0;
106 	dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
107 #if 0
108 	if (dw->dvi.noPolyText)
109 	    dw->dvi.cache.max = 1;
110 #endif
111 	dw->dvi.cache.char_index = 0;
112 	dw->dvi.cache.cache[0].nchars = 0;
113 	dw->dvi.cache.start_x = dw->dvi.cache.x	= XPos (dw);
114 	dw->dvi.cache.start_y = dw->dvi.cache.y = YPos (dw);
115 }
116 
117 void
Newline(DviWidget dw)118 Newline (DviWidget dw)
119 {
120 	FlushCharCache (dw);
121 	dw->dvi.text_x_width = dw->dvi.text_device_width = 0;
122 	dw->dvi.word_flag = 0;
123 }
124 
125 void
Word(DviWidget dw)126 Word (DviWidget dw)
127 {
128 	dw->dvi.word_flag = 1;
129 }
130 
131 #define charWidth(fi,c) (\
132     (fi)->per_char ?\
133 	(fi)->per_char[(c) - (fi)->min_char_or_byte2].width\
134     :\
135 	(fi)->max_bounds.width\
136 )
137 
138 
139 static
charExists(XFontStruct * fi,int c)140 int charExists (XFontStruct *fi, int c)
141 {
142 	XCharStruct *p;
143 
144 	/* `c' is always >= 0 */
145 	if (fi->per_char == NULL
146 	    || (unsigned int)c < fi->min_char_or_byte2
147 	    || (unsigned int)c > fi->max_char_or_byte2)
148 		return 0;
149 	p = fi->per_char + (c - fi->min_char_or_byte2);
150 	return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0
151 		|| p->ascent != 0 || p->descent != 0 || p->attributes != 0);
152 }
153 
154 /* `wid' is in device units */
155 static void
DoCharacter(DviWidget dw,int c,int wid)156 DoCharacter (DviWidget dw, int c, int wid)
157 {
158 	register XFontStruct	*font;
159 	register XTextItem	*text;
160 	int	x, y;
161 
162 	x = XPos(dw);
163 	y = YPos(dw);
164 
165 	/*
166 	 * quick and dirty extents calculation:
167 	 */
168 	if (!(y + 24 >= dw->dvi.extents.y1
169 	      && y - 24 <= dw->dvi.extents.y2
170 #if 0
171 	      && x + 24 >= dw->dvi.extents.x1
172 	      && x - 24 <= dw->dvi.extents.x2
173 #endif
174 	    ))
175 		return;
176 
177 	if (y != dw->dvi.cache.y
178 	    || dw->dvi.cache.char_index >= DVI_CHAR_CACHE_SIZE) {
179 		FlushCharCache (dw);
180 		x = dw->dvi.cache.x;
181 		dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
182 	}
183 	/*
184 	 * load a new font, if the current block is not empty,
185 	 * step to the next.
186 	 */
187 	if (dw->dvi.cache.font_size != dw->dvi.state->font_size ||
188 	    dw->dvi.cache.font_number != dw->dvi.state->font_number)
189 	{
190 		FlushCharCache (dw);
191 		x = dw->dvi.cache.x;
192 		dw->dvi.cache.font_size = dw->dvi.state->font_size;
193 		dw->dvi.cache.font_number = dw->dvi.state->font_number;
194 		dw->dvi.cache.font = QueryFont (dw,
195 						dw->dvi.cache.font_number,
196 						dw->dvi.cache.font_size);
197 		if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
198 			++dw->dvi.cache.index;
199 			if (dw->dvi.cache.index >= dw->dvi.cache.max)
200 				FlushCharCache (dw);
201 			dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
202 			dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
203 		}
204 	}
205 	if (x != dw->dvi.cache.x || dw->dvi.word_flag) {
206 		if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
207 			++dw->dvi.cache.index;
208 			if (dw->dvi.cache.index >= dw->dvi.cache.max)
209 				FlushCharCache (dw);
210 			dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
211 			dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
212 		}
213 		dw->dvi.cache.adjustable[dw->dvi.cache.index]
214 			= dw->dvi.word_flag;
215 		dw->dvi.word_flag = 0;
216 	}
217 	font = dw->dvi.cache.font;
218 	text = &dw->dvi.cache.cache[dw->dvi.cache.index];
219 	if (text->nchars == 0) {
220 		text->chars = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index];
221 		text->delta = x - dw->dvi.cache.x;
222 		if (font != dw->dvi.font) {
223 			text->font = font->fid;
224 			dw->dvi.font = font;
225 		} else
226 			text->font = None;
227 		dw->dvi.cache.x += text->delta;
228 	}
229 	if (charExists(font, c)) {
230 		int w;
231 		dw->dvi.cache.char_cache[dw->dvi.cache.char_index++] = (char) c;
232 		++text->nchars;
233 		w = charWidth(font, c);
234 		dw->dvi.cache.x += w;
235 		if (wid != 0) {
236 			dw->dvi.text_x_width += w;
237 			dw->dvi.text_device_width += wid;
238 		}
239 	}
240 }
241 
242 static
FindCharWidth(DviWidget dw,char * buf,int * widp)243 int FindCharWidth (DviWidget dw, char *buf, int *widp)
244 {
245 	int maxpos;
246 	int i;
247 
248 	if (dw->dvi.device_font == 0
249 	    || dw->dvi.state->font_number != dw->dvi.device_font_number) {
250 		dw->dvi.device_font_number = dw->dvi.state->font_number;
251 		dw->dvi.device_font
252 			= QueryDeviceFont (dw, dw->dvi.device_font_number);
253 	}
254 	if (dw->dvi.device_font
255 	    && device_char_width (dw->dvi.device_font,
256 				  dw->dvi.state->font_size, buf, widp))
257 		return 1;
258 
259 	maxpos = MaxFontPosition (dw);
260 	for (i = 1; i <= maxpos; i++) {
261 		DeviceFont *f = QueryDeviceFont (dw, i);
262 		if (f && device_font_special (f)
263 		    && device_char_width (f, dw->dvi.state->font_size,
264 					  buf, widp)) {
265 			dw->dvi.state->font_number = i;
266 			return 1;
267 		}
268 	}
269 	return 0;
270 }
271 
272 /* Return the width of the character in device units. */
273 
PutCharacter(DviWidget dw,char * buf)274 int PutCharacter (DviWidget dw, char *buf)
275 {
276 	int		prevFont;
277 	int		c = -1;
278 	int		wid = 0;
279 	DviCharNameMap	*map;
280 
281 	if (!dw->dvi.display_enable)
282 		return 0;	/* The width doesn't matter in this case. */
283 	prevFont = dw->dvi.state->font_number;
284 	if (!FindCharWidth (dw, buf, &wid))
285 		return 0;
286 	map = QueryFontMap (dw, dw->dvi.state->font_number);
287 	if (map)
288 		c = DviCharIndex (map, buf);
289 	if (c >= 0)
290 		DoCharacter (dw, c, wid);
291 	else
292 		(void) FakeCharacter (dw, buf, wid);
293 	dw->dvi.state->font_number = prevFont;
294 	return wid;
295 }
296 
297 /* Return 1 if we can fake it; 0 otherwise. */
298 
299 static
FakeCharacter(DviWidget dw,char * buf,int wid)300 int FakeCharacter (DviWidget dw, char *buf, int wid)
301 {
302 	int oldx, oldw;
303 	char ch[2];
304 	const char *chars = 0;
305 
306 	if (buf[0] == '\0' || buf[1] == '\0' || buf[2] != '\0')
307 		return 0;
308 #define pack2(c1, c2) (((c1) << 8) | (c2))
309 
310 	switch (pack2(buf[0], buf[1])) {
311 	case pack2('f', 'i'):
312 		chars = "fi";
313 		break;
314 	case pack2('f', 'l'):
315 		chars = "fl";
316 		break;
317 	case pack2('f', 'f'):
318 		chars = "ff";
319 		break;
320 	case pack2('F', 'i'):
321 		chars = "ffi";
322 		break;
323 	case pack2('F', 'l'):
324 		chars = "ffl";
325 		break;
326 	}
327 	if (!chars)
328 		return 0;
329 	oldx = dw->dvi.state->x;
330 	oldw = dw->dvi.text_device_width;
331 	ch[1] = '\0';
332 	for (; *chars; chars++) {
333 		ch[0] = *chars;
334 		dw->dvi.state->x += PutCharacter (dw, ch);
335 	}
336 	dw->dvi.state->x = oldx;
337 	dw->dvi.text_device_width = oldw + wid;
338 	return 1;
339 }
340 
341 void
PutNumberedCharacter(DviWidget dw,int c)342 PutNumberedCharacter (DviWidget dw, int c)
343 {
344 	char *name;
345 	int wid;
346 	DviCharNameMap	*map;
347 
348 	if (!dw->dvi.display_enable)
349 		return;
350 
351 	if (dw->dvi.device_font == 0
352 	    || dw->dvi.state->font_number != dw->dvi.device_font_number) {
353 		dw->dvi.device_font_number = dw->dvi.state->font_number;
354 		dw->dvi.device_font
355 			= QueryDeviceFont (dw, dw->dvi.device_font_number);
356 	}
357 
358 	if (dw->dvi.device_font == 0
359 	    || !device_code_width (dw->dvi.device_font,
360 				   dw->dvi.state->font_size, c, &wid))
361 		return;
362 	if (dw->dvi.native) {
363 		DoCharacter (dw, c, wid);
364 		return;
365 	}
366 	map = QueryFontMap (dw, dw->dvi.state->font_number);
367 	if (!map)
368 		return;
369 	for (name = device_name_for_code (dw->dvi.device_font, c);
370 	     name;
371 	     name = device_name_for_code ((DeviceFont *)0, c)) {
372 		int code = DviCharIndex (map, name);
373 		if (code >= 0) {
374 			DoCharacter (dw, code, wid);
375 			break;
376 		}
377 		if (FakeCharacter (dw, name, wid))
378 			break;
379 	}
380 }
381 
382 void
ClearPage(DviWidget dw)383 ClearPage (DviWidget dw)
384 {
385 	XClearWindow (XtDisplay (dw), XtWindow (dw));
386 }
387 
388 static void
setGC(DviWidget dw)389 setGC (DviWidget dw)
390 {
391 	int desired_line_width;
392 
393 	if (dw->dvi.line_thickness < 0)
394 		desired_line_width = (int)(((double)dw->dvi.device_resolution
395 					    * dw->dvi.state->font_size)
396 					   / (10.0*72.0*dw->dvi.sizescale));
397 	else
398 		desired_line_width = dw->dvi.line_thickness;
399 
400 	if (desired_line_width != dw->dvi.line_width) {
401 		XGCValues values;
402 		values.line_width = DeviceToX(dw, desired_line_width);
403 		if (values.line_width == 0)
404 			values.line_width = 1;
405 		XChangeGC(XtDisplay (dw), dw->dvi.normal_GC,
406 			  GCLineWidth, &values);
407 		dw->dvi.line_width = desired_line_width;
408 	}
409 }
410 
411 static void
setFillGC(DviWidget dw)412 setFillGC (DviWidget dw)
413 {
414 	int fill_type;
415 	unsigned long mask = GCFillStyle | GCForeground;
416 
417 	fill_type = (dw->dvi.fill * 10) / (DVI_FILL_MAX + 1);
418 	if (dw->dvi.fill_type != fill_type) {
419 		XGCValues values;
420 		if (fill_type <= 0) {
421 			values.foreground = dw->dvi.background;
422 			values.fill_style = FillSolid;
423 		} else if (fill_type >= 9) {
424 			values.foreground = dw->dvi.foreground;
425 			values.fill_style = FillSolid;
426 		} else {
427 			values.foreground = dw->dvi.foreground;
428 			values.fill_style = FillOpaqueStippled;
429 			values.stipple = dw->dvi.gray[fill_type - 1];
430 			mask |= GCStipple;
431 		}
432 		XChangeGC(XtDisplay (dw), dw->dvi.fill_GC, mask, &values);
433 		dw->dvi.fill_type = fill_type;
434 	}
435 }
436 
437 void
DrawLine(DviWidget dw,int x,int y)438 DrawLine (DviWidget dw, int x, int y)
439 {
440 	int xp, yp;
441 
442 	AdjustCacheDeltas (dw);
443 	setGC (dw);
444 	xp = XPos (dw);
445 	yp = YPos (dw);
446 	XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
447 		   xp, yp,
448 		   xp + DeviceToX (dw, x), yp + DeviceToX (dw, y));
449 }
450 
451 void
DrawCircle(DviWidget dw,int diam)452 DrawCircle (DviWidget dw, int diam)
453 {
454 	int d;
455 
456 	AdjustCacheDeltas (dw);
457 	setGC (dw);
458 	d = DeviceToX (dw, diam);
459 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
460 		  XPos (dw), YPos (dw) - d/2,
461 		  d, d, 0, 64*360);
462 }
463 
464 void
DrawFilledCircle(DviWidget dw,int diam)465 DrawFilledCircle (DviWidget dw, int diam)
466 {
467 	int d;
468 
469 	AdjustCacheDeltas (dw);
470 	setFillGC (dw);
471 	d = DeviceToX (dw, diam);
472 	XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
473 		  XPos (dw), YPos (dw) - d/2,
474 		  d, d, 0, 64*360);
475 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
476 		  XPos (dw), YPos (dw) - d/2,
477 		  d, d, 0, 64*360);
478 }
479 
480 void
DrawEllipse(DviWidget dw,int a,int b)481 DrawEllipse (DviWidget dw, int a, int b)
482 {
483 	AdjustCacheDeltas (dw);
484 	setGC (dw);
485 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
486 		  XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
487 		  DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
488 }
489 
490 void
DrawFilledEllipse(DviWidget dw,int a,int b)491 DrawFilledEllipse (DviWidget dw, int a, int b)
492 {
493 	AdjustCacheDeltas (dw);
494 	setFillGC (dw);
495 	XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
496 		  XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
497 		  DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
498 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
499 		  XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
500 		  DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
501 }
502 
503 void
DrawArc(DviWidget dw,int x_0,int y_0,int x_1,int y_1)504 DrawArc (DviWidget dw, int x_0, int y_0, int x_1, int y_1)
505 {
506 	int angle1, angle2;
507 	int rad = (int)((sqrt ((double)x_0*x_0 + (double)y_0*y_0)
508 			 + sqrt ((double)x_1*x_1 + (double)y_1*y_1)
509 			 + 1.0)/2.0);
510 	if ((x_0 == 0 && y_0 == 0) || (x_1 == 0 && y_1 == 0))
511 		return;
512 	angle1 = (int)(atan2 ((double)y_0, (double)-x_0)*180.0*64.0/M_PI);
513 	angle2 = (int)(atan2 ((double)-y_1, (double)x_1)*180.0*64.0/M_PI);
514 
515 	angle2 -= angle1;
516 	if (angle2 < 0)
517 		angle2 += 64*360;
518 
519 	AdjustCacheDeltas (dw);
520 	setGC (dw);
521 
522 	rad = DeviceToX (dw, rad);
523 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
524 		  XPos (dw) + DeviceToX (dw, x_0) - rad,
525 		  YPos (dw) + DeviceToX (dw, y_0) - rad,
526 		  rad*2, rad*2, angle1, angle2);
527 }
528 
529 void
DrawPolygon(DviWidget dw,int * v,int n)530 DrawPolygon (DviWidget dw, int *v, int n)
531 {
532 	XPoint *p;
533 	int i;
534 	int dx, dy;
535 
536 	n /= 2;
537 
538 	AdjustCacheDeltas (dw);
539 	setGC (dw);
540 	p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
541 	p[0].x = XPos (dw);
542 	p[0].y = YPos (dw);
543 	dx = 0;
544 	dy = 0;
545 	for (i = 0; i < n; i++) {
546 		dx += v[2*i];
547 		p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
548 		dy += v[2*i + 1];
549 		p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
550 	}
551 	p[n+1].x = p[0].x;
552 	p[n+1].y = p[0].y;
553 	XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
554 		   p, n + 2, CoordModeOrigin);
555 	XtFree((char *)p);
556 }
557 
558 void
DrawFilledPolygon(DviWidget dw,int * v,int n)559 DrawFilledPolygon (DviWidget dw, int *v, int n)
560 {
561 	XPoint *p;
562 	int i;
563 	int dx, dy;
564 
565 	n /= 2;
566 	if (n < 2)
567 		return;
568 
569 	AdjustCacheDeltas (dw);
570 	setFillGC (dw);
571 	p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
572 	p[0].x = p[n+1].x = XPos (dw);
573 	p[0].y = p[n+1].y = YPos (dw);
574 	dx = 0;
575 	dy = 0;
576 	for (i = 0; i < n; i++) {
577 		dx += v[2*i];
578 		p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
579 		dy += v[2*i + 1];
580 		p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
581 	}
582 	XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
583 		      p, n + 1, Complex, CoordModeOrigin);
584 	XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
585 		      p, n + 2, CoordModeOrigin);
586 	XtFree((char *)p);
587 }
588 
589 #define POINTS_MAX 10000
590 
591 static void
appendPoint(XPoint * points,int * pointi,int x,int y)592 appendPoint(XPoint *points, int *pointi, int x, int y)
593 {
594 	if (*pointi < POINTS_MAX) {
595 		points[*pointi].x = x;
596 		points[*pointi].y = y;
597 		*pointi += 1;
598 	}
599 }
600 
601 #define FLATNESS 1
602 
603 static void
flattenCurve(XPoint * points,int * pointi,int x_2,int y_2,int x_3,int y_3,int x_4,int y_4)604 flattenCurve(XPoint *points, int *pointi,
605 	     int x_2, int y_2, int x_3, int y_3, int x_4, int y_4)
606 {
607 	int x_1, y_1, dx, dy, n1, n2, n;
608 
609 	x_1 = points[*pointi - 1].x;
610 	y_1 = points[*pointi - 1].y;
611 
612 	dx = x_4 - x_1;
613 	dy = y_4 - y_1;
614 
615 	n1 = dy*(x_2 - x_1) - dx*(y_2 - y_1);
616 	n2 = dy*(x_3 - x_1) - dx*(y_3 - y_1);
617 	if (n1 < 0)
618 		n1 = -n1;
619 	if (n2 < 0)
620 		n2 = -n2;
621 	n = n1 > n2 ? n1 : n2;
622 
623 	if (n*n / (dy*dy + dx*dx) <= FLATNESS*FLATNESS)
624 		appendPoint (points, pointi, x_4, y_4);
625 	else {
626 		flattenCurve (points, pointi,
627 			      (x_1 + x_2)/2,
628 			      (y_1 + y_2)/2,
629 			      (x_1 + x_2*2 + x_3)/4,
630 			      (y_1 + y_2*2 + y_3)/4,
631 			      (x_1 + 3*x_2 + 3*x_3 + x_4)/8,
632 			      (y_1 + 3*y_2 + 3*y_3 + y_4)/8);
633 		flattenCurve (points, pointi,
634 			      (x_2 + x_3*2 + x_4)/4,
635 			      (y_2 + y_3*2 + y_4)/4,
636 			      (x_3 + x_4)/2,
637 			      (y_3 + y_4)/2,
638 			      x_4,
639 			      y_4);
640 	}
641 }
642 
643 void
DrawSpline(DviWidget dw,int * v,int n)644 DrawSpline (DviWidget dw, int *v, int n)
645 {
646 	int sx, sy, tx, ty;
647 	int ox, oy, dx, dy;
648 	int i;
649 	int pointi;
650 	XPoint points[POINTS_MAX];
651 
652 	if (n == 0 || (n & 1) != 0)
653 		return;
654 	AdjustCacheDeltas (dw);
655 	setGC (dw);
656 	ox = XPos (dw);
657 	oy = YPos (dw);
658 	dx = v[0];
659 	dy = v[1];
660 	sx = ox;
661 	sy = oy;
662 	tx = sx + DeviceToX (dw, dx);
663 	ty = sy + DeviceToX (dw, dy);
664 
665 	pointi = 0;
666 
667 	appendPoint (points, &pointi, sx, sy);
668 	appendPoint (points, &pointi, (sx + tx)/2, (sy + ty)/2);
669 
670 	for (i = 2; i < n; i += 2) {
671 		int ux = ox + DeviceToX (dw, dx += v[i]);
672 		int uy = oy + DeviceToX (dw, dy += v[i+1]);
673 		flattenCurve (points, &pointi,
674 			       (sx + tx*5)/6, (sy + ty*5)/6,
675 			       (tx*5 + ux)/6, (ty*5 + uy)/6,
676 			       (tx + ux)/2, (ty + uy)/2);
677 		sx = tx;
678 		sy = ty;
679 		tx = ux;
680 		ty = uy;
681 	}
682 
683 	appendPoint (points, &pointi, tx, ty);
684 
685 	XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
686 		   points, pointi, CoordModeOrigin);
687 }
688 
689 
690 /*
691 Local Variables:
692 c-indent-level: 8
693 c-continued-statement-offset: 8
694 c-brace-offset: -8
695 c-argdecl-indent: 8
696 c-label-offset: -8
697 c-tab-always-indent: nil
698 End:
699 */
700