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