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