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