1 /*
2  * $Id: style.i,v 1.1 2005-09-18 22:06:10 dhmunro Exp $
3  * Get and set graphics styles from within the Yorick interpreter.
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 func get_style(&landscape, &systems, &legends, &clegends)
12 /* DOCUMENT get_style, landscape, systems, legends, clegends
13 
14      get the detailed style of the current drawing.  The arguments
15      are all outputs:
16 
17      landscape: 1 if drawing is landscape orientation, 0 if portrait
18      system:    an array of GfakeSystem struct instances, one per
19                 coordinate system in this drawing (ordinarily just one).
20      legends:   a GeLegendBox structure instance describing the layout
21                 of the plot legends
22      clegends:  a GeLegendBox structure instance describing the layout
23                 of the contour legends
24 
25      See the help for the GeLegendBox and GpTextAttribs structs for
26      the details of the legends and clegends arguments.  Basically,
27      you can adjust the location of the legends on the page, the
28      font and height of the characters used to render legends, and
29      whether the legends are split into two columns.
30 
31      The coordinate systems are the systems accessible via the
32      plsys command.  The index of the system in the system array is
33      the index you use to switch to it in the plsys command.  Simple
34      styles have only one coordinate system, and you should carefully
35      consider whether you should design a graphic style with multiple
36      coordinate systems -- most likely, you can do a better job by
37      combining several separate Yorick pictures with some sort of
38      page layout program, rather than trying to do this work within
39      Yorick itself.
40 
41      See the help for the GfakeSystem struct for complete details of
42      what you can adjust.  The most interesting features you can
43      control are the location and aspect ratio of the viewport, and
44      the details of the axis ticks and labels.  The gridxy function
45      provides a simpler interface for fiddling with ticks and labels
46      if that is all you need.  The system.viewport member is the
47      [xmin,xmax,ymin,ymax] of the rectangle on the page where your
48      plots will appear, expressed in NDC coordinates (0.0013 NDC units
49      equals one point, and there are 72.27 points per inch, and 2.54
50      cm per inch; the NDC origin is always at the lower left hand
51      corner of the paper, with x increasing leftward and y increasing
52      upward).  If you change the size of the viewport, you will also
53      need to change the parameters of the tick-generating model; like
54      other problems in typography and page layout, this is harder
55      than you might think.
56 
57    SEE ALSO: set_style, read_style, write_style
58  */
59 {
60   landscape= [0n];
61   n= raw_style(0, landscape, &[], &[]);
62   if (!n) error, "no current drawing";
63   systems= array(GfakeSystem, n);
64   legends= array(GeLegendBox, 2);
65   raw_style, 0, landscape, &systems, &legends;
66   landscape= landscape(1);
67   clegends= legends(2);
68   legends= legends(1);
69 }
70 
set_style(landscape,systems,legends,clegends)71 func set_style(landscape, systems, legends, clegends)
72 /* DOCUMENT set_style, landscape, systems, legends, clegends
73 
74      set the detailed style of the current drawing.  The arguments
75      are all inputs, having the same meanings as for get_style (which
76      see).  All arguments are required, so you may need to call
77      get_style as a starting point, if you only want to make a few
78      changes.  See the Y_SITE/g/work.gs and the other .gs files for
79      examples of reasonable values to choose.
80 
81      Calling set_style destroys anything that was plotted in the
82      window, like the style= keyword of the window command.
83 
84    SEE ALSO: get_style, read_style, write_style
85  */
86 {
87   if (structof(systems)!=GfakeSystem || structof(legends)!=GeLegendBox ||
88       structof(clegends)!=GeLegendBox || numberof(legends)!=1 ||
89       numberof(clegends)!=1 || numberof(landscape)!=1 ||
90       structof(landscape+0)!=long)
91     error, "illegal input data types or sizes";
92   landscape= [int(landscape(1))];
93   raw_style, numberof(systems),  landscape, &systems, &[legends,clegends];
94 }
95 
96 /* ------------------------------------------------------------------------ */
97 
write_style(file,landscape,systems,legends,clegends)98 func write_style(file, landscape, systems, legends, clegends)
99 /* DOCUMENT write_style, file, landscape, systems, legends, clegends
100 
101      write a Gist style sheet (.gs file), using the data structures
102      as described in the get_style function.  The FILE can be a
103      filename or a text file stream.
104 
105    SEE ALSO: get_style, set_style, read_style
106  */
107 {
108   if (structof(file)==string) file= create(file);
109 
110   write,file,format="# %s\n",
111     "Gist style sheet made by Yorick write_style function";
112   write,file,format="# Created: %s\n", timestamp();
113   write,file,format="# (%ld coordinate systems)\n\n", numberof(systems);
114 
115   write,file,format="landscape= %d\n\n", (landscape!=0);
116 
117   for (i=1 ; i<=numberof(systems) ; ++i) {
118     sys= systems(i);
119     if (i==1) {
120       default= sys;
121       which= "default";
122     } else {
123       which= "system";
124     }
125     legend= sys.legend;
126     if (!strlen(legend)) legend= "0";
127     else legend= "\""+legend+"\"";
128     write,file,format="%s= { legend= %s", which, (i>1? legend : "0");
129     final= " }";
130     vp= sys.viewport;
131     if (i==1 || anyof(vp!=default.viewport)) {
132       final= "}";
133       write,file,format=",\n  viewport= { %f, %f, %f, %f }",
134         vp(1),vp(2),vp(3),vp(4);
135     }
136     ticks= sys.ticks;
137     if (i==1 || ticks!=default.ticks) {
138       final= "}";
139       write,file,format=",\n%s\n", "  ticks= {"
140       axes= [ticks.horiz, ticks.vert];
141       daxes= [default.ticks.horiz, default.ticks.vert];
142       prefix= "\n    ";
143       for (j=1 ; j<=2 ; ++j) {
144         axis= axes(j);
145         daxis= daxes(j);
146         if (i==1 || axis!=daxis) {
147           write,file,format="%s%s= {\n", prefix, (j==1? "horiz" : "vert");
148           nitems= 0;
149           prefix= "      ";  suffix= "";
150           if (i==1 || axis.nMajor!=daxis.nMajor) {
151             write,file,format="%snMajor= %f", prefix, axis.nMajor;
152             prefix= ",  ";  suffix= " ";
153             ++nitems;
154           }
155           if (i==1 || axis.nMinor!=daxis.nMinor) {
156             write,file,format="%snMinor= %f", prefix, axis.nMinor;
157             prefix= ",  ";  suffix= " ";
158             ++nitems;
159           }
160           if (i==1 || axis.logAdjMajor!=daxis.logAdjMajor) {
161             write,file,format="%slogAdjMajor= %f", prefix, axis.logAdjMajor;
162             prefix= ",  ";  suffix= " ";
163             if ((++nitems)==3) {
164               nitems= 0;
165               prefix= ",\n      ";
166             }
167           }
168           if (i==1 || axis.logAdjMinor!=daxis.logAdjMinor) {
169             write,file,format="%slogAdjMinor= %f", prefix, axis.logAdjMinor;
170             prefix= ",  ";  suffix= " ";
171             if ((++nitems)==3) {
172               nitems= 0;
173               prefix= ",\n      ";
174             }
175           }
176           if (i==1 || axis.nDigits!=daxis.nDigits) {
177             write,file,format="%snDigits= %d", prefix, axis.nDigits;
178             prefix= ",  ";  suffix= " ";
179             if ((++nitems)==3) {
180               nitems= 0;
181               prefix= ",\n      ";
182             }
183           }
184           if (i==1 || axis.gridLevel!=daxis.gridLevel) {
185             write,file,format="%sgridLevel= %d", prefix, axis.gridLevel;
186             prefix= ",  ";  suffix= " ";
187             if ((++nitems)==3) {
188               nitems= 0;
189               prefix= ",\n      ";
190             }
191           }
192           if (i==1 || axis.flags!=daxis.flags) {
193             write,file,format="%sflags= 0x%03x", prefix, axis.flags;
194             prefix= ",  ";  suffix= " ";
195             if ((++nitems)==3) {
196               nitems= 0;
197               prefix= ",\n      ";
198             }
199           }
200           if (i==1 || axis.tickOff!=daxis.tickOff) {
201             write,file,format="%stickOff= %f", prefix, axis.tickOff;
202             prefix= ",  ";  suffix= " ";
203             if ((++nitems)==3) {
204               nitems= 0;
205               prefix= ",\n      ";
206             }
207           }
208           if (i==1 || axis.labelOff!=daxis.labelOff) {
209             write,file,format="%slabelOff= %f", prefix, axis.labelOff;
210             prefix= ",  ";  suffix= " ";
211             if ((++nitems)==3) {
212               nitems= 0;
213               prefix= ",\n      ";
214             }
215           }
216           if (i==1 || anyof(axis.tickLen!=daxis.tickLen)) {
217             if (prefix!="      ") prefix= ",\n      ";
218             _style_wvect, file,  prefix, "tickLen", axis.tickLen;
219             prefix= ",\n      ";  suffix= "";  nitems= 0;
220           }
221           if (i==1 || axis.tickStyle!=daxis.tickStyle) {
222             if (prefix!="      ") prefix= ",\n      ";
223             _style_wline, file,  prefix, "tickStyle", axis.tickStyle;
224             prefix= ",\n      ";  suffix= "";  nitems= 0;
225           }
226           if (i==1 || axis.gridStyle!=daxis.gridStyle) {
227             if (prefix!="      ") prefix= ",\n      ";
228             _style_wline, file,  prefix, "gridStyle", axis.gridStyle;
229             prefix= ",\n      ";  suffix= "";  nitems= 0;
230           }
231           if (i==1 || axis.textStyle!=daxis.textStyle) {
232             if (prefix!="      ") prefix= ",\n      ";
233             _style_wtext, file,  prefix, "textStyle", axis.textStyle;
234             prefix= ",\n      ";  suffix= "";  nitems= 0;
235           }
236           if (i==1 || axis.xOver!=daxis.xOver) {
237             write,file,format="%sxOver= %f", prefix, axis.xOver;
238             prefix= ",  ";  suffix= " ";
239             if ((++nitems)==3) prefix= ",\n      ";
240           }
241           if (i==1 || axis.yOver!=daxis.yOver) {
242             write,file,format="%syOver= %f", prefix, axis.yOver;
243             suffix= " ";
244           }
245           write,file,format="%s}", suffix;
246           prefix= ",\n\n    ";  suffix= "";
247         }
248       }
249       if (i==1 || ticks.frame!=default.ticks.frame) {
250         write,file,format="%sframe= %d", prefix, ticks.frame;
251         prefix= ",\n    ";  suffix= " ";
252       }
253       if (i==1 || ticks.frameStyle!=default.ticks.frameStyle) {
254         _style_wline, file,  prefix, "frameStyle", ticks.frameStyle;
255         suffix= "";
256       }
257       write,file,format="%s}", suffix;
258     }
259     write,file,format="%s\n", final;
260 
261     if (i==1) {
262       write,file,format="\nsystem= { legend= %s }\n", legend;
263     }
264   }
265 
266   legs= [legends,clegends];
267   for (i=1 ; i<=2 ; ++i) {
268     leg= legs(i);
269     if (leg.nlines) {
270       write,file,format="\n%slegends= {\n", (i==1? "" : "c");
271       write,file,format="  x= %f, y= %f, dx= %f, dy= %f",
272         leg.x, leg.y, leg.dx, leg.dy;
273       _style_wtext, file, ",\n  ", "textStyle", leg.textStyle;
274       write,file,format=",\n  nchars= %d, nlines= %d, nwrap= %d }\n",
275         leg.nchars, leg.nlines, leg.nwrap;
276     } else {
277       write,file,format="\n%slegends= { nlines= 0 }\n", (i==1? "" : "c");
278     }
279   }
280 
281   return file;
282 }
283 
_style_wvect(file,prefix,member,value)284 func _style_wvect(file, prefix, member, value)
285 {
286   write,file,format="%s%s= {", prefix, member;
287   prefix= " ";
288   for (i=1 ; i<=numberof(value) ; ++i) {
289     write,file,format="%s%f", prefix, value(i);
290     prefix= ", ";
291   }
292   write,file,format="%s}", " ";
293 }
294 
_style_wline(file,prefix,member,style)295 func _style_wline(file, prefix, member, style)
296 {
297   write,file,format="%s%s= { color= %d, type= %d, width= %f }",
298     prefix, member, style.color, style.type, style.width;
299 }
300 
_style_wtext(file,prefix,member,style)301 func _style_wtext(file, prefix, member, style)
302 {
303   write,file,format="%s%s= { color= %d, font= 0x%02x, height= %f",
304     prefix, member, style.color, style.font, style.height;
305   if (strpart(prefix,1:1)!=",") {
306     if (strpart(prefix,1:1)!="\n") prefix= ",\n"+prefix;
307     else prefix= ","+prefix;
308   }
309   write,file,format="%s  orient= %d, alignH= %d, alignV= %d, opaque= %d }",
310     prefix, style.orient, style.alignH, style.alignV, style.opaque;
311 }
312 
313 func read_style(file, &landscape, &systems, &legends, &clegends)
314 /* DOCUMENT read_style, file, landscape, systems, legends, clegends
315 
316      read a Gist style sheet (.gs file), and return the data
317      structures as described in the get_style function.  The FILE
318      can be a filename or a text file stream.
319 
320    SEE ALSO: get_style, set_style, write_style
321  */
322 {
323   if (structof(file)==string) {
324     f= open(file, "r", 1);
325     if (!f) {
326       /* maybe the file is in one of the standard locations */
327       f= open("~/gist/"+file, "r", 1);
328       if (!f) {
329         f= open("~/Gist/"+file, "r", 1);
330         if (!f) {
331           f= open(Y_SITE+"g/"+file, "r", 1);
332           if (!f) error, "missing style file: "+file;
333         }
334       }
335     }
336   } else {
337     f= file;
338   }
339 
340   /* set up default values (same as work.gs) */
341   landscape= 0n;
342   default_line= GpLineAttribs(color= 254,  type= 1,  width= 1.0);
343   default_text= GpTextAttribs(
344     color= 254,  font= 0x08,  height= 0.0182,
345     orient= 0,  alignH= 0,  alignV= 0,  opaque= 0);
346   default_ltxt= GpTextAttribs(
347     color= 254,  font= 0x00,  height= 0.0156,
348     orient= 0,  alignH= 1,  alignV= 1,  opaque= 0);
349   default= GfakeSystem(viewport=[0.19, 0.60, 0.44, 0.85],
350     ticks= GaTickStyle(
351       horiz= GaAxisStyle(
352         nMajor=7.5, nMinor=50.0, logAdjMajor=1.2, logAdjMinor=1.2,
353         nDigits=3, gridLevel=1, flags=0x033, tickOff=0.0007, labelOff=0.0182,
354         tickLen= [0.0143, 0.0091, 0.0052, 0.0026, 0.0013],
355         tickStyle= default_line,
356         gridStyle= GpLineAttribs(color= 254,  type= 3,  width= 1.0),
357         textStyle= default_text,
358         xOver= 0.395,  yOver= 0.370),
359       vert= GaAxisStyle(
360         nMajor=7.5, nMinor=50.0, logAdjMajor=1.2, logAdjMinor=1.2,
361         nDigits=3, gridLevel=1, flags=0x033, tickOff=0.0007, labelOff=0.0182,
362         tickLen= [0.0143, 0.0091, 0.0052, 0.0026, 0.0013],
363         tickStyle= default_line,
364         gridStyle= GpLineAttribs(color= 254,  type= 3,  width= 1.0),
365         textStyle= default_text,
366         xOver= 0.150,  yOver= 0.370),
367       frame= 0,
368       frameStyle= GpLineAttribs(color= 254,  type= 1,  width= 1.0)));
369   default_legb= GeLegendBox(
370     x= 0.04698,  y= 0.360,  dx= 0.3758,  dy= 0.0,
371     textStyle= default_ltxt, nchars= 36,  nlines= 20,  nwrap= 2);
372   default_clegb= GeLegendBox(
373     x= 0.6182,  y= 0.8643,  dx= 0.0,  dy= 0.0,
374     textStyle= default_ltxt, nchars= 14,  nlines= 28,  nwrap= 1);
375   legends= default_legb;
376   clegends= default_clegb;
377 
378   /* parse the file */
379   systems= [];
380   type= [];
381   line= "";
382   for (;;) {
383     s= _style_token(f, line, type);
384     if (!line) break;
385     if (type!=3) _style_goof, f, line, s;
386     if (s=="landscape") {
387       landscape= _style_token(f, line, type);
388       if (type!=2 || structof(landscape)!=long)
389         _style_goof, f, line, "landscape= ????";
390       landscape= (landscape!=0);
391     } else if (s=="default") {
392       default= _style_system(f, line, default);
393     } else if (s=="system") {
394       grow, systems, [_style_system(f, line, default)];
395     } else if (s=="legends") {
396       legends= _style_legends(f, line, default_legb);
397     } else if (s=="clegends") {
398       clegends= _style_legends(f, line, default_clegb);
399     } else {
400       _style_goof, f, line, s;
401     }
402   }
403 
404   if (is_void(systems)) systems= [default];
405 }
406 
407 func _style_system(f, &line, default)
408 {
409   system= default;
410   type= [];
411   s= _style_token(f, line, type);
412   if (s!="{") _style_goof, f, line, s;
413   for (;;) {
414     s= _style_token(f, line, type);
415     if (type!=3) _style_goof, f, line, s;
416     if (s=="legend") {
417       s= _style_token(f, line, type);
418       if (type!=1) {
419         if (type==2 && s==0) s= string(0);
420         else _style_goof, f, line, s;
421       }
422       system.legend= s;
423     } else if (s=="viewport") {
424       system.viewport= _style_vector(f,line);
425     } else if (s=="ticks") {
426       system.ticks= _style_ticks(f,line,system.ticks);
427     } else {
428       _style_goof, f, line, s;
429     }
430     s= _style_token(f, line, type);
431     if (s=="}") break;
432     if (s!=",") _style_goof, f, line, s;
433   }
434   return system;
435 }
436 
437 func _style_legends(f, &line, default)
438 {
439   legend= default;
440   type= [];
441   s= _style_token(f, line, type);
442   if (s!="{") _style_goof, f, line, s;
443   for (;;) {
444     s= _style_token(f, line, type);
445     if (type!=3) _style_goof, f, line, s;
446     if (s=="textStyle")
447       get_member(legend,s)= _style_text(f,line,default.textStyle);
448     else
449       get_member(legend,s)= _style_token(f,line,type);
450     s= _style_token(f, line, type);
451     if (s=="}") break;
452     if (s!=",") _style_goof, f, line, s;
453   }
454   return legend;
455 }
456 
457 func _style_ticks(f, &line, default)
458 {
459   ticks= default;
460   type= [];
461   s= _style_token(f, line, type);
462   if (s!="{") _style_goof, f, line, s;
463   for (;;) {
464     s= _style_token(f, line, type);
465     if (type!=3) _style_goof, f, line, s;
466     if (s=="horiz" || s=="vert") {
467       get_member(ticks,s)= _style_axis(f,line,get_member(ticks,s));
468     } else if (s=="frame") {
469       ticks.frame= _style_token(f,line,type);
470     } else if (s=="frameStyle") {
471       ticks.frameStyle= _style_line(f,line,ticks.frameStyle);
472     } else {
473       _style_goof, f, line, s;
474     }
475     s= _style_token(f, line, type);
476     if (s=="}") break;
477     if (s!=",") _style_goof, f, line, s;
478   }
479   return ticks;
480 }
481 
482 func _style_axis(f, &line, default)
483 {
484   axis= default;
485   type= [];
486   s= _style_token(f, line, type);
487   if (s!="{") _style_goof, f, line, s;
488   for (;;) {
489     s= _style_token(f, line, type);
490     if (type!=3) _style_goof, f, line, s;
491     if (s=="tickLen") {
492       value= _style_vector(f, line);
493       axis.tickLen(1:numberof(value))= value;
494     } else if (s=="tickStyle" || s=="gridStyle") {
495       get_member(axis,s)= _style_line(f,line,get_member(axis,s));
496     } else if (s=="textStyle") {
497       axis.textStyle= _style_text(f,line,axis.textStyle);
498     } else {
499       get_member(axis,s)= _style_token(f,line,type);
500     }
501     s= _style_token(f, line, type);
502     if (s=="}") break;
503     if (s!=",") _style_goof, f, line, s;
504   }
505   return axis;
506 }
507 
508 func _style_vector(f, &line)
509 {
510   vector= type= [];
511   s= _style_token(f, line, type);
512   if (s!="{") _style_goof, f, line, s;
513   for (;;) {
514     s= _style_token(f, line, type);
515     if (type!=2) _style_goof, f, line, s;
516     grow, vector, [double(s)];
517     s= _style_token(f, line, type);
518     if (s=="}") break;
519     if (s!=",") _style_goof, f, line, s;
520   }
521   return vector;
522 }
523 
524 func _style_text(f, &line, default)
525 {
526   junk= [0.0];
527   text= default;
528   type= [];
529   s= _style_token(f, line, type);
530   if (s!="{") _style_goof, f, line, s;
531   for (;;) {
532     s= _style_token(f, line, type);
533     if (type!=3) _style_goof, f, line, s;
534     if (text=="path") text= "orient";
535     if (noneof(text==["expand","spacing","upX","upY"]))
536       get_member(text,s)= _style_token(f,line,type);
537     else
538       junk(1)= _style_token(f,line,type);
539     s= _style_token(f, line, type);
540     if (s=="}") break;
541     if (s!=",") _style_goof, f, line, s;
542   }
543   if (text.color < 0) text.color += 256;
544   return text;
545 }
546 
547 func _style_line(f, &line, default)
548 {
549   style= default;
550   type= [];
551   s= _style_token(f, line, type);
552   if (s!="{") _style_goof, f, line, s;
553   for (;;) {
554     s= _style_token(f, line, type);
555     if (type!=3) _style_goof, f, line, s;
556     get_member(style,s)= _style_token(f,line,type);
557     s= _style_token(f, line, type);
558     if (s=="}") break;
559     if (s!=",") _style_goof, f, line, s;
560   }
561   if (style.color < 0) style.color += 256;
562   return style;
563 }
564 
565 /* retrieve next token from file, updating current line
566    -- set line to "" initially
567    -- on output, type= 0 is delimiter, 1 is quoted, 2 is number, 3 keyword
568  */
569 func _style_token(f, &line, &type, norecurse)
570 {
571   s= "";
572   for (;;) {
573     /* give up if no more lines in file */
574     if (!line) return line;
575     /* remove leading whitespace */
576     sread, line, s, format="%[ \t]";
577     line= strpart(line, 1+strlen(s):0);
578     /* stop if line has more chars, and is not a comment */
579     if (strlen(line)) {
580       s= strpart(line,1:1);
581       if (s!="#") break;
582     }
583     /* otherwise get the next line and try again */
584     line= rdline(f);
585   }
586 
587   /* look at first character to see if this is a delimiter */
588   cs= (*pointer(s))(1);
589   if (anyof(['{','}',',','=']==cs)) {
590     /* token is one of the four valid delimiters */
591     line= strpart(line,2:0);
592     type= 0;
593     return string(&[cs,'\0']);
594   } else if (cs=='"') {
595     /* token is a quoted string */
596     s= strtok(line, "\"");
597     type= 1;
598     line= s(2);
599     return s(1);
600   }
601 
602   /* token must be either a number or a keyword */
603   s= strtok(line, " \t{},=")(1);
604   line= strpart(line, strlen(s)+1:0);
605   if ((cs>='0' && cs<='9') || cs=='.' || cs=='-') {
606     /* token is a number */
607     if (strmatch(s,".") || strmatch(s,"e",1)) {
608       value= 0.0;
609       if (!sread(s,value)) _style_goof, f, line, s;
610     } else {
611       value= 0;
612       if (!sread(s,value,format="%i")) _style_goof, f, line, s;
613     }
614     type= 2;
615     return value;
616   } else {
617     /* if next token is =, this token is a keyword */
618     if (!norecurse) next= _style_token(f, line, type, 1);
619     if (type==0 && next=="=") {
620       type= 3;
621       return s;
622     } else {
623       _style_goof, f, line, s;
624     }
625   }
626 }
627 
_style_goof(f,line,s)628 func _style_goof(f, line, s)
629 {
630   write, "graphics style file format error at or just before:";
631   write, print(f)(2);
632   error, "unrecognized token: "+pr1(s);
633 }
634 
635 /* ------------------------------------------------------------------------ */
636 /* The following structure definitions must match those in
637    Gist/gist.h and Gist/draw.h */
638 
639 /* Note: NDC units 0.0013 equals one point equals 1/72.27 inch */
640 one_point= 0.0013;
641 one_inch= 72.27*one_point;
642 
643 struct GpLineAttribs {
644   long color;     /* 255=bg 254=fg 253=b 252=w ...=rgb ...=cmy */
645   int type;       /* line types: 0=none 1=solid 2=- 3=. 4=-. 5=-..  */
646   double width;   /* 1.0 is normal width of a line (1/2 point) */
647 }
648 
649 struct GpTextAttribs {
650   long color;       /* 255=bg 254=fg 253=b 252=w ...=rgb ...=cmy */
651   int font;         /* text font
652                        fonts: 0=courier 4=times 8=helvetica 12=symbol
653                              16=newcentury
654                               or with 1 for bold, 2 for italic */
655   double height;    /* character height in NDC, default 0.0156 (12pt)
656                        UNLIKE GKS, GIST font sizes are always specified
657                        in NDC.  This drastically simplifies coding for
658                        devices like X windows, which must load a font
659                        at each size.  It also conforms better with
660                        a Mac-like user interface in which font size
661                        in points is selected by the user.  */
662   int orient;          /* text orientation: 0=right 1=left 2=up 3=down  */
663   int alignH, alignV;  /* text alignments:
664                           alignH: 0=normal 1=left 2=center 3=right
665                           alignV: 0=normal 1=top 2=cap 3=half 4=base 5=bot */
666 
667   int opaque;
668 }
669 
670 struct GaAxisStyle {
671   double nMajor, nMinor, logAdjMajor, logAdjMinor;
672   int nDigits, gridLevel;
673   int flags;    /* 0x001 ticks on lower edge
674                    0x002 ticks on upper edge
675                    0x004 ticks in center
676                    0x008 inward ticks
677                    0x010 outward ticks
678                    0x020 labels on lower edge
679                    0x040 labels on upper edge
680                    0x080 full grid lines
681                    0x100 origin grid line   */
682 
683   double tickOff, labelOff;  /* offsets in NDC from the edge of the
684                                 viewport to the ticks or labels */
685   double tickLen(5);         /* tick lengths in NDC */
686 
687   GpLineAttribs tickStyle, gridStyle;
688   GpTextAttribs textStyle;   /* alignment ignored, set correctly */
689   double xOver, yOver;       /* position for overflow label */
690 }
691 
692 struct GaTickStyle {
693   GaAxisStyle horiz, vert;
694   int frame;
695   GpLineAttribs frameStyle;
696 }
697 
698 struct GeLegendBox {
699   double x, y;              /* NDC location of this legend box */
700   double dx, dy;            /* if non-zero, offset to 2nd column */
701   GpTextAttribs textStyle;  /* font, size, etc. of these legends */
702   int nchars, nlines;       /* max number of characters per line, lines */
703   int nwrap;                /* max number of lines to wrap long legends */
704 }
705 
706 struct GfakeSystem {
707   double viewport(4);    /* [xmin,xmax,ymin,ymax] in NDC coordinates */
708   GaTickStyle ticks;     /* tick style for this coordinate system */
709   string legend;         /* e.g.- "System 0" or "System 1" */
710 }
711 
712 /* ------------------------------------------------------------------------ */
713