|
|
1.1 root 1: #include <stdio.h>
2: #include "pic.h"
3: #include "y.tab.h"
4: #define dprintf if(dbg)printf
5:
6: #define PI 3.141592654
7: #define PI2 PI/2
8: #define SCALE 200 /* default scale: units/inch */
9: #define WID 150 /* default width for boxes and ellipses */
10: #define HT 100 /* default height and line length */
11:
12: /* But there's one of these in pic.h? -Bart
13: #define MAXOBJ 400
14: */
15: struct obj *objlist[MAXOBJ]; /* store the elements here */
16: int nobj = 0;
17:
18: struct attr attr[40]; /* attributes stored here as collected */
19: int nattr = 0; /* number of entries in attr_list */
20:
21: #define MAXTEXT 200
22: struct text text[MAXTEXT]; /* text strings stored here as collected */
23: int ntext = 0;
24: int ntext1 = 0; /* record ntext here on entry to each figure */
25:
26: #define SYMTAB 200
27: struct symtab symtab[SYMTAB];
28: int nsymtab = 0;
29:
30: coord curx = 0;
31: coord cury = 0;
32:
33: int hvmode = R_DIR; /* R => join left to right, D => top to bottom, etc. */
34:
35: #define MAXTYPE 12
36: int first[MAXTYPE]; /* indicates whether first of particular type */
37: /* has been seen within this picture */
38:
39: int codegen = 0; /* 1=>output for this picture; 0=>no output */
40:
41: struct obj *makenode();
42: char *malloc();
43:
44: float deltx = 6; /* max x value in output, for scaling */
45: float delty = 6; /* max y value in output, for scaling */
46: float scale = 0; /* implies simply scaling by this value; no crop or shift */
47: int dbg = 0;
48: extern FILE *yyin; /* input file pointer */
49: int lineno = 0;
50: char *filename = "-";
51: int synerr = 0;
52: char *cmdname;
53: int crop = 1; /* trim off exterior white space if non-zero */
54:
55: #define DEV202 1
56: #define DEVAPS 2
57: #define DEVCAT 3
58: #define DEV450 4
59:
60: /* You may want to change this if you don't have a 202... */
61:
62: int devtype = DEV202;
63: int res = 972; /* default is 202 */
64: int DX = 4; /* used only for old-style troff */
65: int DY = 4;
66:
67: /* mandatory values for graphic systems CAT: */
68: /*
69: int devtype = DEVCAT;
70: int res = 432;
71: int DX = 3;
72: int DY = 3;
73: */
74:
75: float hshift = 0; /* move this far left for text (in em's) */
76: float vshift = 0.2; /* this far down */
77:
78: coord sxmin; /* lower limit from s command */
79: coord symin;
80: coord sxmax = 4096; /* upper */
81: coord symax = 4096;
82:
83: coord xmin = 30000; /* min values found in actual data */
84: coord ymin = 30000;
85: coord xmax = -30000; /* max */
86: coord ymax = -30000;
87:
88: extern float atof();
89:
90: main(argc,argv)
91: char **argv;
92: {
93: int casel, c;
94:
95: cmdname = argv[0];
96: casel = 0;
97: while (argc > 1 && *argv[1] == '-') {
98: switch (c = argv[1][1]) {
99: case 'T':
100: if (strcmp(&argv[1][2], "aps") == 0) {
101: res = 720;
102: devtype = DEVAPS;
103: DX = DY = 1;
104: } else if (strcmp(&argv[1][2], "cat") == 0) {
105: res = 432;
106: devtype = DEVCAT;
107: DX = DY = 3;
108: } else if (strcmp(&argv[1][2], "450") == 0) {
109: res = 240;
110: devtype = DEV450;
111: } else {
112: res = atoi(&argv[1][2]);
113: }
114: break;
115: case 'c':
116: crop = 0;
117: break;
118: case 'l':
119: delty = atof(&argv[1][2]);
120: casel = 0;
121: break;
122: case 'w':
123: case 's':
124: if (argv[1][2] == 0) {
125: argv++;
126: argc--;
127: deltx = atof(&argv[1][0]);
128: } else
129: deltx = atof(&argv[1][2]);
130: if (c == 's')
131: scale = deltx;
132: break;
133: case 'd':
134: dbg = 1;
135: break;
136: }
137: argc--;
138: argv++;
139: }
140: if (!casel)
141: delty = deltx;
142: setdefaults();
143: if (argc <= 1) {
144: yyin = stdin;
145: getdata(yyin);
146: } else
147: while (argc-- > 1) {
148: if ((yyin = fopen(*++argv, "r")) == NULL) {
149: fprintf(stderr, "pic: can't open %s\n", *argv);
150: exit(1);
151: }
152: filename = *argv;
153: getdata(yyin);
154: fclose(yyin);
155: }
156: exit(0);
157: }
158:
159: setdefaults() /* set default sizes for variables like boxht */
160: {
161: static struct {
162: char *name;
163: int val;
164: } defaults[] ={
165: "lineht", HT,
166: "linewid", HT,
167: "moveht", HT,
168: "movewid", HT,
169: "dashwid", HT/10,
170: "boxht", HT,
171: "boxwid", WID,
172: "circlerad", HT/2,
173: "arcrad", HT/2,
174: "ellipseht", HT,
175: "ellipsewid", WID,
176: "arrowht", HT/5,
177: "arrowwid", HT/10,
178: "textht", HT,
179: "textwid", WID,
180: "scale", SCALE,
181: NULL, 0
182: };
183: int i;
184:
185: for (i = 0; defaults[i].name != NULL; i++)
186: makevar(defaults[i].name, VARNAME, defaults[i].val);
187: }
188:
189: getdata(fin)
190: register FILE *fin;
191: {
192: char buf[1000], buf1[50];
193: FILE *svyyin;
194: int svlineno;
195: char *svfilename, *p;
196:
197: lineno = 0;
198: while (fgets(buf, sizeof buf, fin) != NULL) {
199: lineno++;
200: if (*buf == '.' && *(buf+1) == 'P' && *(buf+2) == 'S') {
201: for (p = &buf[3]; *p == ' '; p++)
202: ;
203: if (*p++ == '<') {
204: svyyin = yyin;
205: svlineno = lineno;
206: svfilename = filename;
207: sscanf(p, "%s", buf1);
208: if ((yyin = fopen(buf1, "r")) == NULL) {
209: fprintf(stderr, "pic: can't open %s\n", buf1);
210: exit(1);
211: }
212: lineno = 0;
213: filename = p;
214: getdata(yyin);
215: fclose(yyin);
216: lineno = svlineno;
217: yyin = svyyin;
218: filename = svfilename;
219: continue;
220: }
221: reset();
222: yyparse();
223: if (buf[3] == ' ') /* assume next thing is width */
224: deltx = delty = atof(&buf[4]);
225: else { /* use scale to determine size */
226: int t;
227: t = xmax - xmin;
228: if (t == 0)
229: t = ymax - ymin;
230: deltx = delty = (float) t / (float)getvar(lookup("scale"));
231: }
232: dprintf("deltx = %.3f\n", deltx);
233: if (codegen && !synerr) {
234: openpl(&buf[3]); /* puts out .PS, with ht & wid stuck in */
235: print(); /* assumes \n at end */
236: closepl(); /* this does the .PE */
237: }
238: }
239: else
240: fputs(buf, stdout);
241: }
242: }
243:
244: print()
245: {
246: struct obj *p;
247: int i, j, m;
248: coord x0, y0, x1, y1, ox, oy, dx, dy;
249:
250: for (i = 0; i < nobj; i++) {
251: p = objlist[i];
252: ox = p->o_x;
253: oy = p->o_y;
254: x1 = p->o_val[0];
255: y1 = p->o_val[1];
256: m = p->o_mode;
257: switch (p->o_type) {
258: case TROFF:
259: troff(text[p->o_nt1].t_val);
260: break;
261: case BOX:
262: move(ox, oy);
263: dotext(p); /* if there are any text strings */
264: x0 = ox - (x1+1) / 2;
265: y0 = oy - (y1+1) / 2;
266: x1 = ox + x1 / 2;
267: y1 = oy + y1 / 2;
268: if (p->o_attr & INVIS)
269: ; /* nothing at all */
270: else if (p->o_dotdash == 0)
271: box(x0, y0, x1, y1);
272: else
273: dotbox(x0, y0, x1, y1, p->o_dotdash, p->o_ddval);
274: if (ishor(m))
275: move(isright(m) ? x1 : x0, oy); /* right side */
276: else
277: move(ox, isdown(m) ? y0 : y1); /* bottom */
278: break;
279: case CIRCLE:
280: move(ox, oy);
281: dotext(p);
282: if ((p->o_attr & INVIS) == 0)
283: circle(ox, oy, x1);
284: if (ishor(m))
285: move(ox + isright(m) ? x1 : -x1, oy);
286: else
287: move(ox, oy + isup(m) ? x1 : -x1);
288: break;
289: case ELLIPSE:
290: move(ox, oy);
291: dotext(p);
292: if ((p->o_attr & INVIS) == 0)
293: ellipse(ox, oy, x1, y1);
294: if (ishor(m))
295: move(ox + isright(m) ? x1 : -x1, oy);
296: else
297: move(ox, oy - isdown(m) ? y1 : -y1);
298: break;
299: case ARC:
300: move(ox, oy);
301: dotext(p);
302: if (p->o_attr & HEAD1)
303: arrow(x1 - (y1 - oy), y1 + (x1 - ox),
304: x1, y1, p->o_val[4], p->o_val[5]);
305: if (p->o_attr & INVIS)
306: /* probably wrong when it's cw */
307: move(x1, y1);
308: else
309: arc(ox, oy, x1, y1, p->o_val[2], p->o_val[3]);
310: if (p->o_attr & HEAD2)
311: arrow(p->o_val[2] + p->o_val[3] - oy, p->o_val[3] - (p->o_val[2] - ox),
312: p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
313: if (p->o_attr & CW_ARC)
314: move(x1, y1); /* because drawn backwards */
315: break;
316: case LINE:
317: case ARROW:
318: case SPLINE:
319: move((ox + x1)/2, (oy + y1)/2); /* center */
320: dotext(p);
321: if (p->o_attr & HEAD1)
322: arrow(ox + p->o_val[5], oy + p->o_val[6], ox, oy, p->o_val[2], p->o_val[3]);
323: if (p->o_attr & INVIS)
324: move(x1, y1);
325: else if (p->o_type == SPLINE)
326: spline(ox, oy, p->o_val[4], &p->o_val[5]);
327: else {
328: int i, j, ndx, ndy;
329: dx = ox;
330: dy = oy;
331: for (i=0, j=5; i < p->o_val[4]; i++, j += 2) {
332: ndx = dx + p->o_val[j];
333: ndy = dy + p->o_val[j+1];
334: if (p->o_dotdash == 0)
335: line(dx, dy, ndx, ndy);
336: else
337: dotline(dx, dy, ndx, ndy, p->o_dotdash, p->o_ddval);
338: dx = ndx;
339: dy = ndy;
340: }
341: }
342: if (p->o_attr & HEAD2) {
343: int i, j;
344: dx = ox;
345: dy = oy;
346: for (i = 0, j = 5; i < p->o_val[4] - 1; i++, j += 2) {
347: dx += p->o_val[j];
348: dy += p->o_val[j+1];
349: }
350: arrow(dx, dy, x1, y1, p->o_val[2], p->o_val[3]);
351: }
352: break;
353: case MOVE:
354: move(ox, oy);
355: dotext(p);
356: break;
357: case TEXT:
358: move(ox, oy);
359: label(x1, y1, 0);
360: free(x1);
361: break;
362: }
363: }
364: }
365:
366: dotline(x0, y0, x1, y1, ddtype, ddval) /* dotted line */
367: coord x0, y0, x1, y1;
368: int ddtype;
369: int ddval;
370: {
371: static int prevval = SCALE/20; /* 20 per inch by default */
372: int i, numdots;
373: double a, b, sqrt(), dx, dy;
374:
375: if (ddval == 0)
376: ddval = prevval;
377: prevval = ddval;
378: /* don't save dot/dash value */
379: dx = x1 - x0;
380: dy = y1 - y0;
381: if (ddtype == DOT) {
382: numdots = sqrt(dx*dx + dy*dy) / prevval + 0.5;
383: for (i = 0; i <= numdots; i++) {
384: a = (float) i / (float) numdots;
385: move(x0 + (int)(a * dx), y0 + (int)(a * dy));
386: dot();
387: }
388: } else if (ddtype == DASH) {
389: double d, dashsize, spacesize;
390: d = sqrt(dx*dx + dy*dy) + 0.5;
391: if (d <= 2 * prevval) {
392: line(x0, y0, x1, y1);
393: return;
394: }
395: numdots = d / (2 * prevval - 1) + 1; /* ceiling */
396: dashsize = prevval;
397: spacesize = (d - numdots * dashsize) / (numdots - 1);
398: for (i = 0; i < numdots-1; i++) {
399: a = i * (dashsize + spacesize) / d;
400: b = a + dashsize / d;
401: line(x0 + (int)(a*dx), y0 + (int)(a*dy), x0 + (int)(b*dx), y0 + (int)(b*dy));
402: a = b;
403: b = a + spacesize / d;
404: move(x0 + (int)(a*dx), y0 + (int)(a*dy));
405: }
406: line(x0 + (int)(b * dx), y0 + (int)(b * dy), x1, y1);
407: }
408: prevval = SCALE/20;
409: }
410:
411: dotbox(x0, y0, x1, y1, ddtype, ddval) /* dotted or dashed box */
412: coord x0, y0, x1, y1;
413: int ddtype;
414: int ddval;
415: {
416: dotline(x0, y0, x1, y0, ddtype, ddval);
417: dotline(x1, y0, x1, y1, ddtype, ddval);
418: dotline(x1, y1, x0, y1, ddtype, ddval);
419: dotline(x0, y1, x0, y0, ddtype, ddval);
420: }
421:
422: dotext(p) /* print text strings of p in proper vertical spacing */
423: struct obj *p;
424: {
425: int i, nhalf;
426:
427: nhalf = p->o_nt2 - p->o_nt1 - 1;
428: for (i = p->o_nt1; i < p->o_nt2; i++) {
429: label(text[i].t_val, text[i].t_type, nhalf);
430: nhalf -= 2;
431: }
432: }
433:
434: reset()
435: {
436: int i;
437:
438: for (i = 0; i < nobj; i++)
439: free(objlist[i]);
440: nobj = 0;
441: nattr = 0;
442: for (i = 0; i < ntext; i++)
443: free(text[i].t_val);
444: ntext = ntext1 = 0;
445: for (i = 0; i < MAXTYPE; i++)
446: first[i] = 0;
447: codegen = synerr = 0;
448: curx = cury = 0;
449: hvmode = R_DIR;
450: sxmin = symin = 0;
451: sxmax = symax = 4096;
452: xmin = ymin = 30000;
453: xmax = ymax = -30000;
454: }
455:
456: coord xstack[20]; /* store curx here for pushdown {...} */
457: coord ystack[20];
458: int hvstack[20];
459: int nstack = 0;
460:
461: push()
462: {
463: xstack[nstack] = curx;
464: ystack[nstack] = cury;
465: hvstack[nstack] = hvmode;
466: nstack++;
467: }
468:
469: pop()
470: {
471: struct obj *p;
472:
473: nstack--;
474: curx = xstack[nstack];
475: cury = ystack[nstack];
476: hvmode = hvstack[nstack];
477: p = makenode(MOVE, 0);
478: dprintf("M %d %d\n", curx, cury);
479: }
480:
481: setvar(n, v, t) /* store v in variable in table slot n */
482: {
483: symtab[n].s_val = v;
484: symtab[n].s_type = t;
485: }
486:
487: getvar(n) /* return value of variable in slot n */
488: {
489: return(symtab[n].s_val);
490: }
491:
492: setdir(n) /* set direction from n */
493: int n;
494: {
495: switch (n) {
496: case UP: hvmode = U_DIR; break;
497: case DOWN: hvmode = D_DIR; break;
498: case LEFT: hvmode = L_DIR; break;
499: case RIGHT: hvmode = R_DIR; break;
500: }
501: return(hvmode);
502: }
503:
504: coord getcomp(p, t) /* return component of a position */
505: struct obj *p;
506: int t;
507: {
508: switch (t) {
509: case DOTX:
510: return(p->o_x);
511: case DOTY:
512: return(p->o_y);
513: case DOTWID:
514: switch (p->o_type) {
515: case BOX:
516: return(p->o_val[0]);
517: case CIRCLE:
518: case ELLIPSE:
519: return(2 * p->o_val[0]);
520: case LINE:
521: case ARROW:
522: return(p->o_val[0] - p->o_x);
523: }
524: case DOTHT:
525: switch (p->o_type) {
526: case BOX:
527: return(p->o_val[1]);
528: case CIRCLE:
529: case ELLIPSE:
530: return(2 * p->o_val[1]);
531: case LINE:
532: case ARROW:
533: return(p->o_val[1] - p->o_y);
534: }
535: case DOTRAD:
536: switch (p->o_type) {
537: case CIRCLE:
538: case ELLIPSE:
539: return(p->o_val[0]);
540: }
541: }
542: }
543:
544:
545: makeattr(type, val) /* add attribute type and val */
546: int type;
547: int val; /* typing probably wrong */
548: {
549: if (type == 0 && val == 0) { /* clear table for next stat */
550: nattr = 0;
551: return;
552: }
553: dprintf("attr %d: %d %d\n", nattr, type, val);
554: attr[nattr].a_type = type;
555: attr[nattr].a_val = val;
556: nattr++;
557: }
558:
559: troffgen(s) /* save away a string of troff commands */
560: char *s;
561: {
562: savetext(CENTER, s); /* use the existing text mechanism */
563: makenode(TROFF, 0);
564: }
565:
566: printexpr(n) /* print expression for debugging */
567: int n;
568: {
569: dprintf("%d\n", n);
570: }
571:
572: struct obj *boxgen(type)
573: {
574: static int prevh = HT;
575: static int prevw = WID; /* golden mean, sort of */
576: int i, invis, at, ddtype, ddval;
577: int with, xwith, ywith;
578: int h, w;
579: coord x0, y0, x1, y1;
580: struct obj *p, *ppos;
581:
582: /* EXPENSIVE! */
583: h = getvar(lookup("boxht"));
584: w = getvar(lookup("boxwid"));
585: invis = at = 0;
586: with = xwith = ywith = 0;
587: ddtype = ddval = 0;
588: for (i = 0; i < nattr; i++) {
589: switch (attr[i].a_type) {
590: case HEIGHT:
591: h = attr[i].a_val;
592: break;
593: case WIDTH:
594: w = attr[i].a_val;
595: break;
596: case SAME:
597: h = prevh;
598: w = prevw;
599: break;
600: case WITH:
601: with = attr[i].a_val; /* corner */
602: break;
603: case AT:
604: ppos = (struct obj *) attr[i].a_val;
605: curx = ppos->o_x;
606: cury = ppos->o_y;
607: at++;
608: break;
609: case INVIS:
610: invis = INVIS;
611: break;
612: case DOT:
613: case DASH:
614: ddtype = attr[i].a_type;
615: ddval = attr[i].a_val;
616: if (ddval == 0)
617: ddval = getvar(lookup("dashwid"));
618: break;
619: case LJUST: case RJUST: case CENTER: case SPREAD: case FILL: case ABOVE: case BELOW:
620: savetext(attr[i].a_type, attr[i].a_val);
621: break;
622: }
623: }
624: if (with) {
625: switch (with) {
626: case NORTH: ywith = -((h+1) / 2); break;
627: case SOUTH: ywith = h / 2; break;
628: case EAST: xwith = -((w+1) / 2); break;
629: case WEST: xwith = w / 2; break;
630: case NE: xwith = -((w+1) / 2); ywith = -((h+1) / 2); break;
631: case SE: xwith = -((w+1) / 2); ywith = h / 2; break;
632: case NW: xwith = w / 2; ywith = -((h+1) / 2); break;
633: case SW: xwith = w / 2; ywith = h / 2; break;
634: }
635: curx += xwith;
636: cury += ywith;
637: }
638: if (!at) {
639: if (isright(hvmode))
640: curx += (w+1) / 2;
641: else if (isleft(hvmode))
642: curx -= w / 2;
643: else if (isup(hvmode))
644: cury += (h+1) / 2;
645: else
646: cury -= h / 2;
647: }
648: x0 = curx - w / 2;
649: y0 = cury - h / 2;
650: x1 = curx + (w+1) / 2;
651: y1 = cury + (h+1) / 2;
652: extreme(x0, y0);
653: extreme(x1, y1);
654: p = makenode(BOX, 2);
655: p->o_val[0] = w;
656: p->o_val[1] = h;
657: p->o_dotdash = ddtype;
658: p->o_ddval = ddval;
659: p->o_attr = invis;
660: dprintf("B %d %d %d %d at %d %d, h=%d, w=%d\n", x0, y0, x1, y1, curx, cury, h, w);
661: if (isright(hvmode))
662: curx = x1;
663: else if (isleft(hvmode))
664: curx = x0;
665: else if (isup(hvmode))
666: cury = y1;
667: else
668: cury = y0;
669: prevh = h;
670: prevw = w;
671: return(p);
672: }
673:
674: savetext(t, s) /* record text elements for current object */
675: int t;
676: char *s;
677: {
678: switch (t) {
679: case CENTER: t = 'C'; break;
680: case LJUST: t = 'L'; break;
681: case RJUST: t = 'R'; break;
682: case SPREAD: t = 'S'; break;
683: case FILL: t = 'F'; break;
684: case ABOVE: t = 'A'; break;
685: case BELOW: t = 'B'; break;
686: }
687: if (ntext >= MAXTEXT) {
688: fprintf(stderr, "too many text strings (%d)\n", ntext);
689: exit(1);
690: }
691: text[ntext].t_type = t;
692: text[ntext].t_val = s;
693: dprintf("saving %c text %s at %d\n", t, s, ntext);
694: ntext++;
695: }
696:
697: struct obj *circgen(type)
698: {
699: static int rad[2] = { HT/2, WID/2 };
700: static int rad2[2] = { HT/2, HT/2 };
701: static coord x0, y0, x1, y1, x2, y2;
702: int i, at, t, invis;
703: int with, xwith, ywith;
704: int r, r2;
705: struct obj *p, *ppos;
706:
707: at = invis = 0;
708: with = xwith = ywith = 0;
709: t = (type == CIRCLE) ? 0 : 1;
710: if (type == CIRCLE)
711: r = r2 = getvar(lookup("circlerad"));
712: else if (type == ELLIPSE) {
713: r = getvar(lookup("ellipsewid")) / 2;
714: r2 = getvar(lookup("ellipseht")) / 2;
715: }
716: for (i = 0; i < nattr; i++)
717: switch (attr[i].a_type) {
718: case LJUST: case RJUST: case CENTER: case SPREAD: case FILL: case ABOVE: case BELOW:
719: savetext(attr[i].a_type, attr[i].a_val);
720: break;
721: case RADIUS:
722: r = attr[i].a_val;
723: break;
724: case DIAMETER:
725: case WIDTH:
726: r = (attr[i].a_val + 1) / 2;
727: break;
728: case HEIGHT:
729: r2 = (attr[i].a_val + 1) / 2;
730: break;
731: case SAME:
732: r = rad[t];
733: r2 = rad2[t];
734: break;
735: case WITH:
736: with = attr[i].a_val;
737: break;
738: case AT:
739: ppos = (struct obj *) attr[i].a_val;
740: curx = ppos->o_x;
741: cury = ppos->o_y;
742: at++;
743: break;
744: case INVIS:
745: invis = INVIS;
746: break;
747: }
748: if (type == CIRCLE)
749: r2 = r; /* probably superfluous */
750: if (with) {
751: switch (with) {
752: case NORTH: ywith = -r2; break;
753: case SOUTH: ywith = r2; break;
754: case EAST: xwith = -r; break;
755: case WEST: xwith = r; break;
756: case NE: xwith = -r * 0.707; ywith = -r2 * 0.707; break;
757: case SE: xwith = -r * 0.707; ywith = r2 * 0.707; break;
758: case NW: xwith = r * 0.707; ywith = -r2 * 0.707; break;
759: case SW: xwith = r * 0.707; ywith = r2 * 0.707; break;
760: }
761: curx += xwith;
762: cury += ywith;
763: }
764: if (!at) {
765: if (isright(hvmode))
766: curx += r;
767: else if (isleft(hvmode))
768: curx -= r;
769: else if (isup(hvmode))
770: cury += r2;
771: else
772: cury -= r2;
773: }
774: p = makenode(type, 2);
775: p->o_val[0] = rad[t] = r;
776: p->o_val[1] = rad2[t] = r2;
777: if (r <= 0 || r2 <= 0) {
778: yyerror("%s has invalid radius %d\n", (type==CIRCLE) ? "circle" : "ellipse", r<r2 ? r : r2);
779: }
780: p->o_attr = invis;
781: extreme(curx+r, cury+r2);
782: extreme(curx-r, cury-r2);
783: if (type == CIRCLE)
784: dprintf("C %d %d %d\n", curx, cury, r);
785: if (type == ELLIPSE)
786: dprintf("E %d %d %d %d\n", curx, cury, r, r2);
787: if (isright(hvmode))
788: curx += r;
789: else if (isleft(hvmode))
790: curx -= r;
791: else if (isup(hvmode))
792: cury += r2;
793: else
794: cury -= r2;
795: return(p);
796: }
797:
798: struct obj *arcgen(type) /* handles circular and (eventually) elliptical arcs */
799: {
800: static int prevw = HT/10;
801: static int prevh = HT/5;
802: static int prevrad = HT/2;
803: static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
804: static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
805: static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
806: static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
807: static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
808: double sqrt(), atan2(), sin(), cos();
809: float dx2, dy2, ht, phi, r, d;
810: int i, head, to, at, cw, invis;
811: struct obj *p, *ppos;
812: coord fromx, fromy, tox, toy;
813:
814: prevrad = getvar(lookup("arcrad"));
815: prevh = getvar(lookup("arrowht"));
816: prevw = getvar(lookup("arrowwid"));
817: fromx = curx;
818: fromy = cury;
819: head = to = at = cw = invis = 0;
820: for (i = 0; i < nattr; i++) {
821: switch (attr[i].a_type) {
822: case LJUST: case RJUST: case CENTER: case SPREAD: case FILL: case ABOVE: case BELOW:
823: savetext(attr[i].a_type, attr[i].a_val);
824: break;
825: case HEAD:
826: head += attr[i].a_val;
827: break;
828: case INVIS:
829: invis = INVIS;
830: break;
831: case HEIGHT: /* length of arrowhead */
832: prevh = attr[i].a_val;
833: break;
834: case WIDTH: /* width of arrowhead */
835: prevw = attr[i].a_val;
836: break;
837: case RADIUS:
838: prevrad = attr[i].a_val;
839: break;
840: case DIAMETER:
841: prevrad = attr[i].a_val / 2;
842: break;
843: case CW:
844: cw = 1;
845: break;
846: case FROM: /* start point of arc */
847: ppos = (struct obj *) attr[i].a_val;
848: fromx = ppos->o_x;
849: fromy = ppos->o_y;
850: break;
851: case TO: /* end point of arc */
852: ppos = (struct obj *) attr[i].a_val;
853: tox = ppos->o_x;
854: toy = ppos->o_y;
855: to++;
856: break;
857: case AT: /* center of arc */
858: ppos = (struct obj *) attr[i].a_val;
859: curx = ppos->o_x;
860: cury = ppos->o_y;
861: at = 1;
862: break;
863: case UP:
864: hvmode = U_DIR;
865: break;
866: case DOWN:
867: hvmode = D_DIR;
868: break;
869: case RIGHT:
870: hvmode = R_DIR;
871: break;
872: case LEFT:
873: hvmode = L_DIR;
874: break;
875: }
876: }
877: if (!at && !to) { /* the defaults are mostly OK */
878: curx = fromx + prevrad * dctrx[cw][hvmode];
879: cury = fromy + prevrad * dctry[cw][hvmode];
880: tox = fromx + prevrad * dtox[cw][hvmode];
881: toy = fromy + prevrad * dtoy[cw][hvmode];
882: hvmode = nexthv[cw][hvmode];
883: }
884: else if (!at) {
885: dx2 = (float)(tox - fromx) / 2;
886: dy2 = (float)(toy - fromy) / 2;
887: phi = atan2(dy2, dx2) + (cw ? -PI2 : PI2);
888: for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
889: ; /* this kludge gets around too-small radii */
890: ht = sqrt(d);
891: curx = fromx + dx2 + ht * cos(phi) + 0.5;
892: cury = fromy + dy2 + ht * sin(phi) + 0.5;
893: dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
894: dx2, dy2, phi, r, ht);
895: }
896: else if (at && !to) { /* do we have all the cases??? */
897: tox = fromx + prevrad * dtox[cw][hvmode];
898: toy = fromy + prevrad * dtoy[cw][hvmode];
899: hvmode = nexthv[cw][hvmode];
900: }
901: if (cw) { /* interchange roles of from-to and heads */
902: coord temp;
903: temp = fromx; fromx = tox; tox = temp;
904: temp = fromy; fromy = toy; toy = temp;
905: if (head == HEAD1)
906: head = HEAD2;
907: else if (head == HEAD2)
908: head = HEAD1;
909: }
910: p = makenode(type, 6);
911: /* these are wrong in general */
912: extreme(fromx, fromy);
913: extreme(tox, toy);
914: p->o_val[0] = fromx;
915: p->o_val[1] = fromy;
916: p->o_val[2] = tox;
917: p->o_val[3] = toy;
918: if (cw) {
919: curx = fromx;
920: cury = fromy;
921: } else {
922: curx = tox;
923: cury = toy;
924: }
925: p->o_val[4] = prevw;
926: p->o_val[5] = prevh;
927: p->o_attr = head | (cw ? CW_ARC : 0) | invis;
928: dprintf("arc at %d %d from %d %d to %d %d head %d %d\n",
929: p->o_x, p->o_y,
930: p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
931: return(p);
932: }
933:
934: struct obj *linegen(type)
935: {
936: static int prevdx = HT;
937: static int prevdy = 0;
938: static int prevw = HT/10;
939: static int prevh = HT/5;
940: int i, j, some, head, ddtype, ddval, invis;
941: int chop, chop1, chop2, x0, y0, x1, y1;
942: double sin(), cos(), atan2(), theta;
943: int defx, defy;
944: struct obj *p, *ppos;
945: static int xtab[] = { 1, 0, -1, 0 }; /* R=0, U=1, L=2, D=3 */
946: static int ytab[] = { 0, 1, 0, -1 };
947: int ndxy, dx[50], dy[50];
948: coord nx, ny;
949:
950: nx = curx;
951: ny = cury;
952: defx = getvar(lookup("linewid"));
953: defy = getvar(lookup("lineht"));
954: prevh = getvar(lookup("arrowht"));
955: prevw = getvar(lookup("arrowwid"));
956: dx[0] = dy[0] = ndxy = some = head = invis = 0;
957: chop = chop1 = chop2 = 0;
958: ddtype = ddval = 0;
959: for (i = 0; i < nattr; i++) {
960: switch (attr[i].a_type) {
961: case LJUST: case RJUST: case CENTER: case SPREAD: case FILL: case ABOVE: case BELOW:
962: savetext(attr[i].a_type, attr[i].a_val);
963: break;
964: case HEAD:
965: head += attr[i].a_val;
966: break;
967: case INVIS:
968: invis = INVIS;
969: break;
970: case CHOP:
971: if (chop++ == 0)
972: chop1 = chop2 = attr[i].a_val;
973: else
974: chop2 = attr[i].a_val;
975: break;
976: case DOT:
977: case DASH:
978: ddtype = attr[i].a_type;
979: ddval = attr[i].a_val;
980: if (ddval == 0)
981: ddval = getvar(lookup("dashwid"));
982: break;
983: case SAME:
984: dx[ndxy] = prevdx;
985: dy[ndxy] = prevdy;
986: some++;
987: break;
988: case LEFT:
989: dx[ndxy] -= (attr[i].a_val==0) ? defx : attr[i].a_val;
990: some++;
991: hvmode = L_DIR;
992: break;
993: case RIGHT:
994: dx[ndxy] += (attr[i].a_val==0) ? defx : attr[i].a_val;
995: some++;
996: hvmode = R_DIR;
997: break;
998: case UP:
999: dy[ndxy] += (attr[i].a_val==0) ? defy : attr[i].a_val;
1000: some++;
1001: hvmode = U_DIR;
1002: break;
1003: case DOWN:
1004: dy[ndxy] -= (attr[i].a_val==0) ? defy : attr[i].a_val;
1005: some++;
1006: hvmode = D_DIR;
1007: break;
1008: case HEIGHT: /* length of arrowhead */
1009: prevh = attr[i].a_val;
1010: break;
1011: case WIDTH: /* width of arrowhead */
1012: prevw = attr[i].a_val;
1013: break;
1014: case TO:
1015: if (some) {
1016: nx += dx[ndxy];
1017: ny += dy[ndxy];
1018: ndxy++;
1019: dx[ndxy] = dy[ndxy] = some = 0;
1020: }
1021: ppos = (struct obj *) attr[i].a_val;
1022: dx[ndxy] = ppos->o_x - nx;
1023: dy[ndxy] = ppos->o_y - ny;
1024: some++;
1025: break;
1026: case BY:
1027: ppos = (struct obj *) attr[i].a_val;
1028: dx[ndxy] = ppos->o_x;
1029: dy[ndxy] = ppos->o_y;
1030: some++;
1031: break;
1032: case THEN: /* turn off any previous accumulation */
1033: if (some) {
1034: nx += dx[ndxy];
1035: ny += dy[ndxy];
1036: ndxy++;
1037: dx[ndxy] = dy[ndxy] = some = 0;
1038: }
1039: break;
1040: case FROM:
1041: case AT:
1042: ppos = (struct obj *) attr[i].a_val;
1043: nx = curx = ppos->o_x;
1044: ny = cury = ppos->o_y;
1045: break;
1046: }
1047: }
1048: if (some) {
1049: nx += dx[ndxy];
1050: ny += dy[ndxy];
1051: ndxy++;
1052: defx = dx[ndxy-1];
1053: defy = dy[ndxy-1];
1054: } else {
1055: defx *= xtab[hvmode];
1056: defy *= ytab[hvmode];
1057: dx[ndxy] = defx;
1058: dy[ndxy] = defy;
1059: ndxy++;
1060: nx += defx;
1061: ny += defy;
1062: }
1063: prevdx = defx;
1064: prevdy = defy;
1065: if (chop) {
1066: if (chop == 1 && chop1 == 0) /* just said "chop", so use default */
1067: chop1 = chop2 = getvar(lookup("circlerad"));
1068: theta = atan2((float) defy, (float) defx);
1069: x0 = chop1 * cos(theta);
1070: y0 = chop1 * sin(theta);
1071: curx += x0;
1072: cury += y0;
1073: x1 = chop2 * cos(theta);
1074: y1 = chop2 * sin(theta);
1075: nx -= x1;
1076: ny -= y1;
1077: dx[0] -= x0;
1078: dy[0] -= y0;
1079: dx[ndxy-1] -= x1;
1080: dy[ndxy-1] -= y1;
1081: if(dbg)printf("chopping %d %d %d %d; cur=%d,%d end=%d,%d\n",
1082: x0, y0, x1, y1, curx, cury, nx, ny);
1083: }
1084: p = makenode(type, 5 + 2 * ndxy);
1085: curx = p->o_val[0] = nx;
1086: cury = p->o_val[1] = ny;
1087: if (head || type == ARROW) {
1088: p->o_val[2] = prevw;
1089: p->o_val[3] = prevh;
1090: if (head == 0)
1091: head = HEAD2; /* default arrow head */
1092: }
1093: p->o_attr = head | invis;
1094: p->o_val[4] = ndxy;
1095: nx = p->o_x;
1096: ny = p->o_y;
1097: for (i = 0, j = 5; i < ndxy; i++, j += 2) {
1098: p->o_val[j] = dx[i];
1099: p->o_val[j+1] = dy[i];
1100: extreme(nx += dx[i], ny += dy[i]);
1101: }
1102: p->o_dotdash = ddtype;
1103: p->o_ddval = ddval;
1104: if (dbg) {
1105: printf("S or L from %d %d to %d %d with %d elements:\n", p->o_x, p->o_y, curx, cury, ndxy);
1106: for (i = 0, j = 5; i < ndxy; i++, j += 2)
1107: printf("%d %d\n", p->o_val[j], p->o_val[j+1]);
1108: }
1109: extreme(p->o_x, p->o_y);
1110: extreme(curx, cury);
1111: return(p);
1112: }
1113:
1114: struct obj *splinegen(type)
1115: {
1116: linegen(type);
1117: }
1118:
1119: struct obj *movegen(type)
1120: {
1121: static int prevdx, prevdy;
1122: int i, dx, dy, some;
1123: int defx, defy;
1124: struct obj *p;
1125: struct obj *ppos;
1126: static int xtab[] = { 1, 0, -1, 0 }; /* R=0, U=1, L=2, D=3 */
1127: static int ytab[] = { 0, 1, 0, -1 };
1128:
1129: defx = getvar(lookup("movewid"));
1130: defy = getvar(lookup("moveht"));
1131: dx = dy = some = 0;
1132: for (i = 0; i < nattr; i++) {
1133: switch (attr[i].a_type) {
1134: case LJUST: case RJUST: case CENTER: case SPREAD: case FILL: case ABOVE: case BELOW:
1135: savetext(attr[i].a_type, attr[i].a_val);
1136: break;
1137: case SAME:
1138: dx = prevdx;
1139: dy = prevdy;
1140: some++;
1141: break;
1142: case LEFT:
1143: dx -= (attr[i].a_val==0) ? defx : attr[i].a_val;
1144: some++;
1145: hvmode = L_DIR;
1146: break;
1147: case RIGHT:
1148: dx += (attr[i].a_val==0) ? defx : attr[i].a_val;
1149: some++;
1150: hvmode = R_DIR;
1151: break;
1152: case UP:
1153: dy += (attr[i].a_val==0) ? defy : attr[i].a_val;
1154: some++;
1155: hvmode = U_DIR;
1156: break;
1157: case DOWN:
1158: dy -= (attr[i].a_val==0) ? defy : attr[i].a_val;
1159: some++;
1160: hvmode = D_DIR;
1161: break;
1162: case TO:
1163: ppos = (struct obj *) attr[i].a_val;
1164: dx = ppos->o_x - curx;
1165: dy = ppos->o_y - cury;
1166: some++;
1167: break;
1168: case BY:
1169: ppos = (struct obj *) attr[i].a_val;
1170: dx = ppos->o_x;
1171: dy = ppos->o_y;
1172: some++;
1173: break;
1174: case FROM:
1175: case AT:
1176: ppos = (struct obj *) attr[i].a_val;
1177: curx = ppos->o_x;
1178: cury = ppos->o_y;
1179: break;
1180: }
1181: }
1182: if (some) {
1183: defx = dx;
1184: defy = dy;
1185: } else {
1186: defx *= xtab[hvmode];
1187: defy *= ytab[hvmode];
1188: }
1189: prevdx = defx;
1190: prevdy = defy;
1191: extreme(curx, cury);
1192: curx += defx;
1193: cury += defy;
1194: extreme(curx, cury);
1195: p = makenode(MOVE, 0);
1196: dprintf("M %d %d\n", curx, cury);
1197: return(p);
1198: }
1199:
1200: struct obj *textgen(s, garb)
1201: {
1202: static int prevdx, prevdy, prevrad = 10;
1203: int i, dx, dy, some, type;
1204: struct obj *p, *ppos;
1205:
1206: type = 'C';
1207: some = dx = dy = 0;
1208: for (i = 0; i < nattr; i++)
1209: switch (attr[i].a_type) {
1210: case LEFT:
1211: dx -= attr[i].a_val;
1212: some++;
1213: break;
1214: case RIGHT:
1215: dx += attr[i].a_val;
1216: some++;
1217: break;
1218: case UP:
1219: dy += attr[i].a_val;
1220: some++;
1221: break;
1222: case DOWN:
1223: dy -= attr[i].a_val;
1224: some++;
1225: break;
1226: case AT:
1227: ppos = (struct obj *) attr[i].a_val;
1228: curx = ppos->o_x;
1229: cury = ppos->o_y;
1230: break;
1231: case LJUST:
1232: type = 'L';
1233: break;
1234: case RJUST:
1235: type = 'R';
1236: break;
1237: case SPREAD:
1238: type = 'S';
1239: break;
1240: case FILL:
1241: type = 'F';
1242: break;
1243: case ABOVE:
1244: type = 'A';
1245: break;
1246: case BELOW:
1247: type = 'B';
1248: break;
1249: }
1250: dprintf("T %c %s\n", type, s);
1251: if (some) {
1252: prevdx = dx;
1253: prevdy = dy;
1254: } else {
1255: dx = prevdx;
1256: dy = prevdy;
1257: }
1258: extreme(curx, cury);
1259: curx += dx;
1260: cury += dy;
1261: extreme(curx, cury); /* wrong */
1262: p = makenode(TEXT, 2);
1263: p->o_val[0] = s;
1264: p->o_val[1] = type;
1265: return(p);
1266: }
1267:
1268: char *tostring(s)
1269: register char *s;
1270: {
1271: register char *p;
1272:
1273: p = malloc(strlen(s)+1);
1274: if (p == NULL) {
1275: fprintf(stderr, "pic: out of space in tostring on %s", s);
1276: exit(1);
1277: }
1278: strcpy(p, s);
1279: return(p);
1280: }
1281:
1282: makevar(s, t, v) /* make variable named s in table */
1283: char *s;
1284: int t;
1285: int v; /* maybe wrong later */
1286: {
1287: int i;
1288:
1289: for (i = 0; i < nsymtab; i++)
1290: if (strcmp(s, symtab[i].s_name) == 0)
1291: return(i);
1292: if (nsymtab >= SYMTAB)
1293: yyerror("symbol table overflow (%d)", SYMTAB);
1294: symtab[nsymtab].s_name = tostring(s);
1295: symtab[nsymtab].s_type = t;
1296: symtab[nsymtab].s_val = v;
1297: return(nsymtab++);
1298: }
1299:
1300: lookup(s) /* find s in symtab */
1301: char *s;
1302: {
1303: int i;
1304:
1305: for (i = 0; i < nsymtab; i++)
1306: if (strcmp(s, symtab[i].s_name) == 0)
1307: return(i);
1308: return(-1);
1309: }
1310:
1311: struct obj *makepos(x, y) /* make a position cell */
1312: coord x;
1313: coord y;
1314: {
1315: struct obj *p;
1316:
1317: p = makenode(PLACE, 0);
1318: p->o_x = x;
1319: p->o_y = y;
1320: return(p);
1321: }
1322:
1323: struct obj *getpos(p, corner) /* find position of point */
1324: struct obj *p;
1325: int corner;
1326: {
1327: coord x, y, x1, y1;
1328:
1329: dprintf("getpos %o %d\n", p, corner);
1330: x = p->o_x;
1331: y = p->o_y;
1332: x1 = p->o_val[0];
1333: y1 = p->o_val[1];
1334: switch (p->o_type) {
1335: case PLACE:
1336: break;
1337: case BOX:
1338: switch (corner) {
1339: case NORTH: y += y1 / 2; break;
1340: case SOUTH: y -= y1 / 2; break;
1341: case EAST: x += x1 / 2; break;
1342: case WEST: x -= x1 / 2; break;
1343: case NE: x += x1 / 2; y += y1 / 2; break;
1344: case SW: x -= x1 / 2; y -= y1 / 2; break;
1345: case SE: x += x1 / 2; y -= y1 / 2; break;
1346: case NW: x -= x1 / 2; y += y1 / 2; break;
1347: }
1348: break;
1349: case CIRCLE:
1350: case ELLIPSE:
1351: switch (corner) {
1352: case NORTH: y += y1; break;
1353: case SOUTH: y -= y1; break;
1354: case EAST: x += x1; break;
1355: case WEST: x -= x1; break;
1356: case NE: x += 0.707 * x1; y += 0.707 * y1; break;
1357: case SE: x += 0.707 * x1; y -= 0.707 * y1; break;
1358: case NW: x -= 0.707 * x1; y += 0.707 * y1; break;
1359: case SW: x -= 0.707 * x1; y -= 0.707 * y1; break;
1360: }
1361: break;
1362: case LINE:
1363: case ARROW:
1364: case MOVE:
1365: switch (corner) {
1366: case START: break; /* already in place */
1367: case END: x = x1; y = y1; break;
1368: case CENTER: x = (x+x1)/2; y = (y+y1)/2; break;
1369: case NORTH: if (y1 > y) { x = x1; y = y1; } break;
1370: case SOUTH: if (y1 < y) { x = x1; y = y1; } break;
1371: case EAST: if (x1 > x) { x = x1; y = y1; } break;
1372: case WEST: if (x1 < x) { x = x1; y = y1; } break;
1373: }
1374: break;
1375: case ARC:
1376: switch (corner) {
1377: case START:
1378: if (p->o_attr & CW_ARC) {
1379: x = p->o_val[2]; y = p->o_val[3];
1380: } else {
1381: x = x1; y = y1;
1382: }
1383: break;
1384: case END:
1385: if (p->o_attr & CW_ARC) {
1386: x = x1; y = y1;
1387: } else {
1388: x = p->o_val[2]; y = p->o_val[3];
1389: }
1390: break;
1391: }
1392: break;
1393: case SPLINE:
1394: switch (corner) {
1395: case END:
1396: x = p->o_val[0];
1397: y = p->o_val[1];
1398: break;
1399: }
1400: break;
1401: }
1402: dprintf("getpos returns %d %d\n", x, y);
1403: return(makepos(x, y));
1404: }
1405:
1406: struct obj *gethere(n) /* make a place for curx,cury */
1407: {
1408: dprintf("gethere %d %d\n", curx, cury);
1409: return(makepos(curx, cury));
1410: }
1411:
1412: struct obj *getlast(n, t) /* find n-th previous occurrence of type t */
1413: int n, t;
1414: {
1415: int i, k;
1416: struct obj *p;
1417:
1418: k = n;
1419: for (i = nobj-1; i >= 0; i--) {
1420: p = objlist[i];
1421: if (p->o_type != t)
1422: continue;
1423: if (--k > 0)
1424: continue; /* not there yes */
1425: dprintf("got a last of x,y= %d,%d\n", p->o_x, p->o_y);
1426: return(p);
1427: }
1428: yyerror("there is no %dth last", n);
1429: }
1430:
1431: struct obj *getfirst(n, t) /* find n-th occurrence of type t */
1432: int n, t;
1433: {
1434: int i, k;
1435: struct obj *p;
1436:
1437: k = n;
1438: for (i = 0; i < nobj; i++) {
1439: p = objlist[i];
1440: if (p->o_type != t)
1441: continue;
1442: if (--k > 0)
1443: continue; /* not there yes */
1444: dprintf("got a first of x,y= %d,%d\n", p->o_x, p->o_y);
1445: return(p);
1446: yyerror("there is no %dth ", n);
1447: }
1448: }
1449:
1450: struct obj *fixpos(p, x, y)
1451: struct obj *p;
1452: coord x, y;
1453: {
1454: dprintf("fixpos returns %d %d\n", p->o_x + x, p->o_y + y);
1455: return(makepos(p->o_x + x, p->o_y + y));
1456: }
1457:
1458: struct obj *makenode(type, n)
1459: int type, n;
1460: {
1461: struct obj *p;
1462:
1463: p = (struct obj *) malloc(sizeof(struct obj) + (n-1)*sizeof(coord));
1464: if (p == NULL) {
1465: fprintf(stderr, "pic: out of space in makenode\n");
1466: exit(1);
1467: }
1468: p->o_type = type;
1469: p->o_count = n;
1470: p->o_mode = hvmode;
1471: p->o_x = curx;
1472: p->o_y = cury;
1473: p->o_nt1 = ntext1;
1474: p->o_nt2 = ntext;
1475: ntext1 = ntext; /* ready for next caller */
1476: p->o_attr = p->o_dotdash = p->o_ddval = 0;
1477: if (nobj >= MAXOBJ) {
1478: fprintf(stderr, "pic: objlist overflow\n");
1479: exit(1);
1480: }
1481: objlist[nobj++] = p;
1482: return(p);
1483: }
1484:
1485: extreme(x, y) /* record max and min x and y values */
1486: {
1487: if (x > xmax)
1488: xmax = x;
1489: if (y > ymax)
1490: ymax = y;
1491: if (x < xmin)
1492: xmin = x;
1493: if (y < ymin)
1494: ymin = y;
1495: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.