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