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