]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/grap/ticks.c
aux/realemu: run cpuproc in same fd group as fileserver
[plan9front.git] / sys / src / cmd / grap / ticks.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include "grap.h"
6 #include "y.tab.h"
7
8 #define MAXTICK 200
9 int     ntick   = 0;
10 double  tickval[MAXTICK];       /* tick values (one axis at a time */
11 char    *tickstr[MAXTICK];      /* and labels */
12
13 int     tside   = 0;
14 int     tlist   = 0;            /* 1 => explicit values given */
15 int     toffside = 0;           /* no ticks on these sides */
16 int     goffside = 0;           /* no ticks on grid on these sides */
17 int     tick_dir = OUT;
18 double  ticklen = TICKLEN;      /* default tick length */
19 int     autoticks = LEFT|BOT;
20 int     autodir = 0;            /* set LEFT, etc. if automatic ticks go in */
21
22 void savetick(double f, char *s)        /* remember tick location and label */
23 {
24         if (ntick >= MAXTICK)
25                 ERROR "too many ticks (%d)", MAXTICK FATAL;
26         tickval[ntick] = f;
27         tickstr[ntick] = s;
28         ntick++;
29 }
30
31 void dflt_tick(double f)
32 {
33         if (f >= 0.0)
34                 savetick(f, tostring("%g"));
35         else
36                 savetick(f, tostring("\\%g"));
37 }
38
39 void tickside(int n)    /* remember which side these ticks/gridlines go on */
40 {
41         tside |= n;
42 }
43
44 void tickoff(int side)  /* remember explicit sides */
45 {
46         toffside |= side;
47 }
48
49 void gridtickoff(void)  /* turn grid ticks off on the side previously specified (ugh) */
50 {
51         goffside = tside;
52 }
53
54 void setlist(void)      /* remember that there was an explicit list */
55 {
56         tlist = 1;
57 }
58
59 void tickdir(int dir, double val, int explicit) /* remember in/out [expr] */
60 {
61         tick_dir = dir;
62         if (explicit)
63                 ticklen = val;
64 }
65
66 void ticks(void)                /* set autoticks after ticks statement */
67 {
68         /* was there an explicit "ticks [side] off"? */
69         if (toffside)
70                 autoticks &= ~toffside;
71         /* was there an explicit list? (eg "ticks at ..." or "ticks from ...") */
72         if (tlist) {
73                 if (tside & (BOT|TOP))
74                         autoticks &= ~(BOT|TOP);
75                 if (tside & (LEFT|RIGHT))
76                         autoticks &= ~(LEFT|RIGHT);
77         }
78         /* was there a side without a list? (eg "ticks left in") */
79         if (tside && !tlist) {
80                 if (tick_dir == IN)
81                         autodir |= tside;
82                 if (tside & (BOT|TOP))
83                         autoticks = (autoticks & ~(BOT|TOP)) | (tside & (BOT|TOP));
84                 if (tside & (LEFT|RIGHT))
85                         autoticks = (autoticks & ~(LEFT|RIGHT)) | (tside & (LEFT|RIGHT));
86         }
87         tlist = tside = toffside = goffside = 0;
88         tick_dir = OUT;
89 }
90
91 double modfloor(double f, double t)
92 {
93         t = fabs(t);
94         return floor(f/t) * t;
95 }
96
97 double modceil(double f, double t)
98 {
99         t = fabs(t);
100         return ceil(f/t) * t;
101 }
102
103 double  xtmin, xtmax;   /* range of ticks */
104 double  ytmin, ytmax;
105 double  xquant, xmult;  /* quantization & scale for auto x ticks */
106 double  yquant, ymult;
107 double  lograt = 5;
108
109 void do_autoticks(Obj *p)       /* make set of ticks for default coord only */
110 {
111         double x, xl, xu, q;
112
113         if (p == NULL)
114                 return;
115         fprintf(tfd, "Autoticks:\t# x %g..%g, y %g..%g",
116                 p->pt.x, p->pt1.x, p->pt.y, p->pt1.y);
117         fprintf(tfd, ";   xt %g,%g, yt %g,%g, xq,xm = %g,%g, yq,ym = %g,%g\n",
118                 xtmin, xtmax, ytmin, ytmax, xquant, xmult, yquant, ymult);
119         if ((autoticks & (BOT|TOP)) && p->pt1.x >= p->pt.x) {   /* make x ticks */
120                 q = xquant;
121                 xl = p->pt.x;
122                 xu = p->pt1.x;
123                 if (xl >= xu)
124                         dflt_tick(xl);
125                 else if ((p->log & XFLAG) && xu/xl >= lograt) {
126                         for (x = q; x < xu; x *= 10) {
127                                 logtick(x, xl, xu);
128                                 if (xu/xl <= 100) {
129                                         logtick(2*x, xl, xu);
130                                         logtick(5*x, xl, xu);
131                                 }
132                         }
133                 } else {
134                         xl = modceil(xtmin - q/100, q);
135                         xu = modfloor(xtmax + q/100, q) + q/2;
136                         for (x = xl; x <= xu; x += q)
137                                 dflt_tick(x);
138                 }
139                 tside = autoticks & (BOT|TOP);
140                 ticklist(p, 0);
141         }
142         if ((autoticks & (LEFT|RIGHT)) && p->pt1.y >= p->pt.y) {        /* make y ticks */
143                 q = yquant;
144                 xl = p->pt.y;
145                 xu = p->pt1.y;
146                 if (xl >= xu)
147                         dflt_tick(xl);
148                 else if ((p->log & YFLAG) && xu/xl >= lograt) {
149                         for (x = q; x < xu; x *= 10) {
150                                 logtick(x, xl, xu);
151                                 if (xu/xl <= 100) {
152                                         logtick(2*x, xl, xu);
153                                         logtick(5*x, xl, xu);
154                                 }
155                         }
156                 } else {
157                         xl = modceil(ytmin - q/100, q);
158                         xu = modfloor(ytmax + q/100, q) + q/2;
159                         for (x = xl; x <= xu; x += q)
160                                 dflt_tick(x);
161                 }
162                 tside = autoticks & (LEFT|RIGHT);
163                 ticklist(p, 0);
164         }
165 }
166
167 void logtick(double v, double lb, double ub)
168 {
169         float slop = 1.0;       /* was 1.001 */
170
171         if (slop * lb <= v && ub >= slop * v)
172                 dflt_tick(v);
173 }
174
175 Obj *setauto(void)      /* compute new min,max, and quant & mult */
176 {
177         Obj *p, *q;
178
179         if ((q = lookup("lograt",0)) != NULL)
180                 lograt = q->fval;
181         for (p = objlist; p; p = p->next)
182                 if (p->type == NAME && strcmp(p->name,dflt_coord) == 0)
183                         break;
184         if (p) {
185                 if ((p->log & XFLAG) && p->pt1.x/p->pt.x >= lograt)
186                         autolog(p, 'x');
187                 else
188                         autoside(p, 'x');
189                 if ((p->log & YFLAG) && p->pt1.y/p->pt.y >= lograt)
190                         autolog(p, 'y');
191                 else
192                         autoside(p, 'y');
193         }
194         return p;
195 }
196
197 void autoside(Obj *p, int side)
198 {
199         double r, s, d, ub, lb;
200
201         if (side == 'x') {
202                 xtmin = lb = p->pt.x;
203                 xtmax = ub = p->pt1.x;
204         } else {
205                 ytmin = lb = p->pt.y;
206                 ytmax = ub = p->pt1.y;
207         }
208         if (ub <= lb)
209                 return; /* cop out on little ranges */
210         d = ub - lb;
211         r = s = 1;
212         while (d * s < 10)
213                 s *= 10;
214         d *= s;
215         while (10 * r < d)
216                 r *= 10;
217         if (r > d/3)
218                 r /= 2;
219         else if (r <= d/6)
220                 r *= 2;
221         if (side == 'x') {
222                 xquant = r / s;
223         } else {
224                 yquant = r / s;
225         }
226 }
227
228 void autolog(Obj *p, int side)
229 {
230         double r, s, t, ub, lb;
231         int flg;
232
233         if (side == 'x') {
234                 xtmin = lb = p->pt.x;
235                 xtmax = ub = p->pt1.x;
236                 flg = p->coord & XFLAG;
237         } else {
238                 ytmin = lb = p->pt.y;
239                 ytmax = ub = p->pt1.y;
240                 flg = p->coord & YFLAG;
241         }
242         for (s = 1; lb * s < 1; s *= 10)
243                 ;
244         lb *= s;
245         ub *= s;
246         for (r = 1; 10 * r < lb; r *= 10)
247                 ;
248         for (t = 1; t < ub; t *= 10)
249                 ;
250         if (side == 'x')
251                 xquant = r / s;
252         else
253                 yquant = r / s;
254         if (flg)
255                 return;
256         if (ub / lb < 100) {
257                 if (lb >= 5 * r)
258                         r *= 5;
259                 else if (lb >= 2 * r)
260                         r *= 2;
261                 if (ub * 5 <= t)
262                         t /= 5;
263                 else if (ub * 2 <= t)
264                         t /= 2;
265                 if (side == 'x') {
266                         xtmin = r / s;
267                         xtmax = t / s;
268                 } else {
269                         ytmin = r / s;
270                         ytmax = t / s;
271                 }
272         }
273 }
274
275 void iterator(double from, double to, int op, double by, char *fmt)     /* create an iterator */
276 {
277         double x;
278
279         /* should validate limits, etc. */
280         /* punt for now */
281
282         dprintf("iterate from %g to %g by %g, op = %c, fmt=%s\n",
283                 from, to, by, op, fmt ? fmt : "");
284         switch (op) {
285         case '+':
286         case ' ':
287                 for (x = from; x <= to + (SLOP-1) * by; x += by)
288                         if (fmt)
289                                 savetick(x, tostring(fmt));
290                         else
291                                 dflt_tick(x);
292                 break;
293         case '-':
294                 for (x = from; x >= to; x -= by)
295                         if (fmt)
296                                 savetick(x, tostring(fmt));
297                         else
298                                 dflt_tick(x);
299                 break;
300         case '*':
301                 for (x = from; x <= SLOP * to; x *= by)
302                         if (fmt)
303                                 savetick(x, tostring(fmt));
304                         else
305                                 dflt_tick(x);
306                 break;
307         case '/':
308                 for (x = from; x >= to; x /= by)
309                         if (fmt)
310                                 savetick(x, tostring(fmt));
311                         else
312                                 dflt_tick(x);
313                 break;
314         }
315         if (fmt)
316                 free(fmt);
317 }
318
319 void ticklist(Obj *p, int explicit)     /* fire out the accumulated ticks */
320                                         /* 1 => list, 0 => auto */
321 {
322         if (p == NULL)
323                 return;
324         fprintf(tfd, "Ticks_%s:\n\tticklen = %g\n", p->name, ticklen);
325         print_ticks(TICKS, explicit, p, "ticklen", "");
326 }
327
328 void print_ticks(int type, int explicit, Obj *p, char *lenstr, char *descstr)
329 {
330         int i, logflag, inside;
331         char buf[100];
332         double tv;
333
334         for (i = 0; i < ntick; i++)     /* any ticks given explicitly? */
335                 if (tickstr[i] != NULL)
336                         break;
337         if (i >= ntick && type == TICKS)        /* no, so use values */
338                 for (i = 0; i < ntick; i++) {
339                         if (tickval[i] >= 0.0)
340                                 sprintf(buf, "%g", tickval[i]);
341                         else
342                                 sprintf(buf, "\\-%g", -tickval[i]);
343                         tickstr[i] = tostring(buf);
344                 }
345         else
346                 for (i = 0; i < ntick; i++) {
347                         if (tickstr[i] != NULL) {
348                                 sprintf(buf, tickstr[i], tickval[i]);
349                                 free(tickstr[i]);
350                                 tickstr[i] = tostring(buf);
351                         }
352                 }
353         logflag = sidelog(p->log, tside);
354         for (i = 0; i < ntick; i++) {
355                 tv = tickval[i];
356                 halfrange(p, tside, tv);
357                 if (logflag) {
358                         if (tv <= 0.0)
359                                 ERROR "can't take log of tick value %g", tv FATAL;
360                         logit(tv);
361                 }
362                 if (type == GRID)
363                         inside = LEFT|RIGHT|TOP|BOT;
364                 else if (explicit)
365                         inside = (tick_dir == IN) ? tside : 0;
366                 else
367                         inside = autodir;
368                 if (tside & BOT)
369                         maketick(type, p->name, BOT, inside, tv, tickstr[i], lenstr, descstr);
370                 if (tside & TOP)
371                         maketick(type, p->name, TOP, inside, tv, tickstr[i], lenstr, descstr);
372                 if (tside & LEFT)
373                         maketick(type, p->name, LEFT, inside, tv, tickstr[i], lenstr, descstr);
374                 if (tside & RIGHT)
375                         maketick(type, p->name, RIGHT, inside, tv, tickstr[i], lenstr, descstr);
376                 if (tickstr[i]) {
377                         free(tickstr[i]);
378                         tickstr[i] = NULL;
379                 }
380         }
381         ntick = 0;
382 }
383
384 void maketick(int type, char *name, int side, int inflag, double val, char *lab, char *lenstr, char *descstr)
385 {
386         char *sidestr, *td;
387
388         fprintf(tfd, "\tline %s ", descstr);
389         inflag &= side;
390         switch (side) {
391         case BOT:
392         case 0:
393                 td = inflag ? "up" : "down";
394                 fprintf(tfd, "%s %s from (x_%s(%g),0)", td, lenstr, name, val);
395                 break;
396         case TOP:
397                 td = inflag ? "down" : "up";
398                 fprintf(tfd, "%s %s from (x_%s(%g),frameht)", td, lenstr, name, val);
399                 break;
400         case LEFT:
401                 td = inflag ? "right" : "left";
402                 fprintf(tfd, "%s %s from (0,y_%s(%g))", td, lenstr, name, val);
403                 break;
404         case RIGHT:
405                 td = inflag ? "left" : "right";
406                 fprintf(tfd, "%s %s from (framewid,y_%s(%g))", td, lenstr, name, val);
407                 break;
408         }
409         fprintf(tfd, "\n");
410         if (type == GRID && (side & goffside))  /* wanted no ticks on grid */
411                 return;
412         sidestr = tick_dir == IN ? "start" : "end";
413         if (lab != NULL) {
414                 /* BUG: should fix size of lab here */
415                 double wid = strlen(lab)/7.5 + (tick_dir == IN ? 0 : 0.1);      /* estimate width at 15 chars/inch */
416                 switch (side) {
417                 case BOT: case 0:
418                         /* can drop "box invis" with new pic */
419                         fprintf(tfd, "\tbox invis \"%s\" ht .25 wid 0 with .n at last line.%s",
420                                 lab, sidestr);
421                         break;
422                 case TOP:
423                         fprintf(tfd, "\tbox invis \"%s\" ht .2 wid 0 with .s at last line.%s",
424                                 lab, sidestr);
425                         break;
426                 case LEFT:
427                         fprintf(tfd, "\t\"%s \" wid %.2f rjust at last line.%s",
428                                 lab, wid, sidestr);
429                         break;
430                 case RIGHT:
431                         fprintf(tfd, "\t\" %s\" wid %.2f ljust at last line.%s",
432                                 lab, wid, sidestr);
433                         break;
434                 }
435                 /* BUG: works only if "down x" comes before "at wherever" */
436                 lab_adjust();
437                 fprintf(tfd, "\n");
438         }
439 }
440
441 Attr    *grid_desc      = 0;
442
443 void griddesc(Attr *a)
444 {
445         grid_desc = a;
446 }
447
448 void gridlist(Obj *p)
449 {
450         char *framestr;
451
452         if ((tside & (BOT|TOP)) || tside == 0)
453                 framestr = "frameht";
454         else
455                 framestr = "framewid";
456         fprintf(tfd, "Grid_%s:\n", p->name);
457         tick_dir = IN;
458         print_ticks(GRID, 0, p, framestr, desc_str(grid_desc));
459         if (grid_desc) {
460                 freeattr(grid_desc);
461                 grid_desc = 0;
462         }
463 }
464
465 char *desc_str(Attr *a) /* convert DOT to "dotted", etc. */
466 {
467         static char buf[50], *p;
468
469         if (a == NULL)
470                 return p = "";
471         switch (a->type) {
472         case DOT:       p = "dotted"; break;
473         case DASH:      p = "dashed"; break;
474         case INVIS:     p = "invis"; break;
475         default:        p = "";
476         }
477         if (a->fval != 0.0) {
478                 sprintf(buf, "%s %g", p, a->fval);
479                 return buf;
480         } else
481                 return p;
482 }
483
484 sidelog(int logflag, int side)  /* figure out whether to scale a side */
485 {
486         if ((logflag & XFLAG) && ((side & (BOT|TOP)) || side == 0))
487                 return 1;
488         else if ((logflag & YFLAG) && (side & (LEFT|RIGHT)))
489                 return 1;
490         else
491                 return 0;
492 }