1 /*
2  * $Id: graph.c,v 1.12 2010-04-13 11:34:30 thiebaut Exp $
3  * Define interactive graphics interface using Gist graphics package.
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 #include "gist.h"
12 #include "xbasic.h"
13 #include "hlevel.h"
14 
15 /* primitive allowance for other non-X windows systems */
16 #ifndef DISPLAY_ENGINE
17 # define DISPLAY_ENGINE GpFXEngine
18 #endif
19 #ifdef NO_XLIB
20 # define NO_MOUSE
21 static int my_rgb_read(Engine *eng, GpColor *rgb, long *nx, long *ny);
22 # define RGB_READER my_rgb_read
23 #else
24 # ifndef NO_MOUSE
25 #  ifndef DISPLAY_MOUSE
26 #   define DISPLAY_MOUSE GxPointClick
27 #  endif
28 # endif
29 # define RGB_READER g_rgb_read
30 extern unsigned long gx_parent;
31 #endif
32 #ifdef NO_MOUSE
33 # ifndef DISPLAY_ZOOM_FACTOR
34 #  define DISPLAY_ZOOM_FACTOR myZoomFactor
35 static double myZoomFactor= 1.0;
36 # endif
37 #endif
38 #ifndef DISPLAY_ZOOM_FACTOR
39 # define DISPLAY_ZOOM_FACTOR gxZoomFactor
40 #endif
41 
42 /* various X headers included by xfancy.h define True and False */
43 #undef True
44 #undef False
45 
46 #include "ydata.h"
47 #include "yio.h"
48 #include "pstdlib.h"
49 #include "play.h"
50 #include <string.h>
51 
52 /* Quining operation defined in parse.c */
53 extern void YpQuine(char *name, int nQuined);
54 extern int PutsAsArray(char *s);              /* from yio.c */
55 /* in graph0.c */
56 extern long QuickMeshZone(double xx, double yy, double *x, double *y,
57                           int *reg, long ix, long ijx, long i,
58                           long *bndy, long nbndy);
59 extern long *BuildMeshBndy(double *x, double *y, int *reg,
60                            long ix, long ijx, long *nbndy);
61 
62 extern BuiltIn Y_plg, Y_plm, Y_plc, Y_plv, Y_plf, Y_pli, Y_plt, Y_pldj;
63 extern BuiltIn Y_plmesh, Y_bytscl, Y_plfp;
64 
65 extern BuiltIn Y_limits, Y_logxy, Y_zoom_factor, Y_unzoom;
66 /* Note: range function is interpreted shell for limits */
67 
68 extern BuiltIn Y_window, Y_hcp_file, Y_hcp_finish, Y_plsys, Y_palette;
69 extern BuiltIn Y_window_geometry, Y_window_exists, Y_window_select,
70   Y_window_list;
71 
72 extern BuiltIn Y_fma, Y_redraw, Y_hcp, Y_hcpon, Y_hcpoff, Y_animate;
73 
74 extern BuiltIn Y_plq, Y_pledit, Y_pldefault, Y_gridxy;
75 
76 extern BuiltIn Y__pl_init;  /* called at initialization by graph.i */
77 
78 extern BuiltIn Y_mouse, Y_contour, Y_mesh_loc, Y_pause, Y_current_window;
79 extern BuiltIn Y_keybd_focus, Y_rgb_read;
80 extern BuiltIn Y_current_mouse, Y_set_gpath;
81 
82 /*--------------------------------------------------------------------------*/
83 
84 static void FreeReference(void *obj);
85 static void Safe_free(void *vptr);
86 
87 static char *SetHCPname(int n, char *name);
88 static char *GetHCPname(int n);
89 static char *hcpNames[GH_NDEVS+1];
90 static void SetHCPDefault(void);
91 static void CheckDefaultWindow(void);
92 static void CheckDefaultPalette(void);
93 
94 static double *Get1Ddouble(Symbol *stack, long *length);
95 static double *Get2Ddouble(Symbol *stack, long *len1, long *len2);
96 static int *Get2Dint(Symbol *stack, long *len1, long *len2);
97 static short *Get2Dshort(Symbol *stack, long *len1, long *len2);
98 static double *GetDouble(Symbol *stack, long *n);
99 static void LegendAndHide(char *func, char *arg1, char *arg2, char *arg3,
100                           char *arg4, Symbol *keySymbols[]);
101 static int GetLineType(Symbol *stack);
102 static int YgetColor(Symbol *stack);
103 static int GetFont(Symbol *stack);
104 static int GetTypeface(char *s);
105 static void GetJustify(Symbol *stack);
106 static long Safe_strlen(const char *s);
107 static void AllocTmpLegend(long len);
108 static void FreeTmpLegend(void);
109 static long escape_count(char *arg);
110 static void escape_cat(char *leg, char *arg);
111 static Symbol *GrabMesh(Symbol *stack, Symbol *triKey, GaQuadMesh *mesh,
112                         char **y_name, char **x_name, char **r_name, int tmp);
113 static int *PadRegionArray(Symbol *stack, int *reg, long iMax, long jMax);
114 static void *CopyArray(Symbol *stack, void *xOld,
115                        StructDef *base, long iMax, long jMax);
116 static double *CopyLevels(double *levels, long nLevels);
117 static void GetZCrange(double *zmn, double *zmx, double *z, int *reg,
118                        int region, long iMax, long jMax, int zCompressed);
119 static void GetPCrange(double *zmn, double *zmx, double *z, int *reg,
120                        int region, long iMax, long jMax);
121 static void GrabByteScale(Symbol **keySymbols, double *scale, double *offset,
122                           double *zn, double *zx, double *z, int *reg,
123                           int region, long iMax, long jMax, int zCompressed);
124 static GpColor *PushColors(double *z, long len, double zmin, double zmax,
125                            double scale, double offset);
126 
127 static void PrintHideLegend(char *line, int type);
128 static void PrintColor(char *line, int color, int suffix);
129 static void PrintTypeWidth(char *line, int suffix);
130 static void PrintMarks(char *line, int suffix);
131 static void PrintSuffix(int suffix);
132 static void PrintRegion(char *line, int suffix);
133 static double Safe_dbl(double x);
134 static void *MakePropArray(StructDef *base, long size);
135 
136 static void CheckPalette(void);
137 
138 static int maxColors= 200;  /* maximum number of colors for GpReadPalette */
139 static int hcpDump= 1;      /* whiners can't figure out how to dump colors */
140 static int hcpPSdefault= 1;
141 static int hcpOnFMA= 0;
142 static int defaultDPI= 75;
143 static int defaultLegends= 1;
144 static char *defaultStyle= 0;
145 static char *defaultPalette= 0;
146 
147 /* Pointers to the default mesh set with plmesh are actually
148    Yorick Array pointees.  */
149 static long iMesh= 0, jMesh= 0;
150 static double *xMesh= 0, *yMesh= 0;
151 static int *regMesh= 0;
152 static short *triangleMesh= 0;
153 
154 static int curElement= -1;
155 
156 static void RefMesh(GaQuadMesh *mesh);
157 
158 extern int YCurrentPlotter(void); /* for style.c */
159 
160 /*--------------------------------------------------------------------------*/
161 
162 static long yOrigin;
163 
Get1Ddouble(Symbol * stack,long * length)164 static double *Get1Ddouble(Symbol *stack, long *length)
165 {
166   Operand op;
167   long n= *length;
168   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
169   stack->ops->FormOperand(stack, &op);
170   if (op.ops==&voidOps) return 0;
171   if (op.ops->promoteID>T_DOUBLE || (op.type.dims && op.type.dims->next))
172     YError("expecting 1D array convertable to type double as argument");
173   op.ops->ToDouble(&op);
174   if (n>0 && op.type.number!=n)
175     YError("1D double array must be same length as a previous argument");
176   if (op.type.dims) yOrigin= op.type.dims->origin;
177   else yOrigin= 1L;
178   *length= op.type.number;
179   return (double *)op.value;
180 }
181 
Get2Ddouble(Symbol * stack,long * len1,long * len2)182 static double *Get2Ddouble(Symbol *stack, long *len1, long *len2)
183 {
184   Operand op;
185   long n1= *len1;
186   long n2= *len2;
187   Dimension *next= 0;
188   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
189   stack->ops->FormOperand(stack, &op);
190   if (op.ops==&voidOps) return 0;
191   if (op.ops->promoteID>T_DOUBLE ||
192       !op.type.dims || !(next= op.type.dims->next) || next->next)
193     YError("expecting 2D array convertable to type double as argument");
194   op.ops->ToDouble(&op);
195   if (n1>0 && (op.type.dims->number!=n1 || next->number!=n2))
196     YError("2D double array must be same shape as a previous argument");
197   *len1= op.type.dims->number;
198   *len2= op.type.dims->next->number;
199   return (double *)op.value;
200 }
201 
Get2Dint(Symbol * stack,long * len1,long * len2)202 static int *Get2Dint(Symbol *stack, long *len1, long *len2)
203 {
204   Operand op;
205   long n1= *len1;
206   long n2= *len2;
207   Dimension *next= 0;
208   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
209   stack->ops->FormOperand(stack, &op);
210   if (op.ops==&voidOps) return 0;
211   if (op.ops->promoteID>T_LONG ||
212       !op.type.dims || !(next= op.type.dims->next) || next->next)
213     YError("expecting 2D array convertable to type int as argument");
214   op.ops->ToInt(&op);
215   if (n1>0 && (op.type.dims->number!=n1 || next->number!=n2))
216     YError("2D int array must be same shape as a previous argument");
217   *len1= op.type.dims->number;
218   *len2= op.type.dims->next->number;
219   return (int *)op.value;
220 }
221 
Get2Dshort(Symbol * stack,long * len1,long * len2)222 static short *Get2Dshort(Symbol *stack, long *len1, long *len2)
223 {
224   Operand op;
225   long n1= *len1;
226   long n2= *len2;
227   Dimension *next= 0;
228   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
229   stack->ops->FormOperand(stack, &op);
230   if (op.ops==&voidOps) return 0;
231   if (op.ops->promoteID>T_LONG ||
232       !op.type.dims || !(next= op.type.dims->next) || next->next)
233     YError("expecting 2D array convertable to type short as argument");
234   op.ops->ToShort(&op);
235   if (n1>0 && (op.type.dims->number!=n1 || next->number!=n2))
236     YError("2D short array must be same shape as a previous argument");
237   *len1= op.type.dims->number;
238   *len2= op.type.dims->next->number;
239   return (short *)op.value;
240 }
241 
Safe_strlen(const char * s)242 static long Safe_strlen(const char *s)
243 {
244   if (s) return strlen(s);
245   else return 0;
246 }
247 
248 static char *tmpLegend = 0;
249 
250 static void
AllocTmpLegend(long len)251 AllocTmpLegend(long len)
252 {
253   if (tmpLegend) FreeTmpLegend();
254   tmpLegend = p_malloc(len+1);
255   tmpLegend[0] = '\0';
256 }
257 
FreeTmpLegend(void)258 static void FreeTmpLegend(void)
259 {
260   if (tmpLegend) {
261     char *legend= tmpLegend;
262     tmpLegend= 0;
263     p_free(legend);
264   }
265 }
266 
escape_count(char * arg)267 static long escape_count(char *arg)
268 {
269   long n= 0;
270   if (arg) while (*arg) {
271     if (*arg=='!' || *arg=='_' || *arg=='^') n++;
272     arg++;
273   }
274   return n;
275 }
276 
escape_cat(char * leg,char * arg)277 static void escape_cat(char *leg, char *arg)
278 {
279   while (*arg) {
280     if (*arg=='!' || *arg=='_' || *arg=='^') *(leg++)= '!';
281     *(leg++)= *(arg++);
282   }
283   *leg= '\0';
284 }
285 
LegendAndHide(char * func,char * arg1,char * arg2,char * arg3,char * arg4,Symbol * keySymbols[])286 static void LegendAndHide(char *func, char *arg1, char *arg2, char *arg3,
287                           char *arg4, Symbol *keySymbols[])
288 {
289   /* check for hide= keyword */
290   if (YNotNil(keySymbols[1])) gistD.hidden= (YGetInteger(keySymbols[1])!=0);
291   else gistD.hidden= 0;
292 
293   if (tmpLegend) FreeTmpLegend();
294 
295   /* check for legend= keyword -- put legend into tmpLegend */
296   if (keySymbols[0]) {
297     /* legend=[] is same as legend=string() */
298     Symbol *stack= keySymbols[0];
299     if (YNotNil(stack)) tmpLegend= p_strcpy(YGetString(stack));
300 
301   } else if (func) {
302     /* construct default legend from up to 4 quined arguments */
303     long len0= Safe_strlen(func);
304     long len1= Safe_strlen(arg1)+escape_count(arg1);
305     long len2= Safe_strlen(arg2)+escape_count(arg2);
306     long len3= Safe_strlen(arg3)+escape_count(arg3);
307     long len4= Safe_strlen(arg4)+escape_count(arg4);
308     AllocTmpLegend(len0+len1+len2+len3+len4+6);
309     if (func) strcat(tmpLegend, func);
310     if (arg1) {
311       escape_cat(tmpLegend+len0, arg1);
312       len0+= len1;
313       if (arg2) {
314         strcat(tmpLegend+len0, ", ");
315         escape_cat(tmpLegend+len0+2, arg2);
316         len0+= 2+len2;
317         if (arg3) {
318           strcat(tmpLegend+len0, ", ");
319           escape_cat(tmpLegend+len0+2, arg3);
320           len0+= 2+len3;
321           if (arg4) {
322             strcat(tmpLegend+len0, ", ");
323             escape_cat(tmpLegend+len0+2, arg4);
324             len0+= 2+len4;
325           }
326         }
327       }
328     }
329   }
330 
331   /* Put tmpLegend into gistD.legend -- it will be copied out when the
332      element is created.  Only danger is pledit, since GdEdit just
333      copies the pointer, not the string -- handle this case specially.  */
334   gistD.legend= tmpLegend;
335 }
336 
GetLineType(Symbol * stack)337 static int GetLineType(Symbol *stack)
338 {
339   Operand op;
340   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
341   stack->ops->FormOperand(stack, &op);
342   if (op.ops==&stringOps) {
343     char *s= ((char **)op.value)[0];
344     if (op.type.dims) YError("illegal line type -- need scalar string");
345     if (strcmp(s, "none")==0) return L_NONE;
346     else if (strcmp(s, "solid")==0) return L_SOLID;
347     else if (strcmp(s, "dash")==0) return L_DASH;
348     else if (strcmp(s, "dot")==0) return L_DOT;
349     else if (strcmp(s, "dashdot")==0) return L_DASHDOT;
350     else if (strcmp(s, "dashdotdot")==0) return L_DASHDOTDOT;
351     YError("unrecognized line type keyword");
352     return 0;
353   } else {
354     int type= (int)YGetInteger(stack);
355     if (type<0) type= 0;
356     else if (type>5) type= 1 + (type-1)%5;
357     return type;
358   }
359 }
360 
361 static int
YgetColor(Symbol * stack)362 YgetColor(Symbol *stack)
363 {
364   Operand op;
365   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
366   stack->ops->FormOperand(stack, &op);
367   if (op.ops==&stringOps) {
368     char *s= ((char **)op.value)[0];
369     if (op.type.dims) YError("illegal color -- need scalar string");
370     if (strcmp(s, "bg")==0) return P_BG;
371     else if (strcmp(s, "fg")==0) return P_FG;
372     else if (strcmp(s, "black")==0) return P_BLACK;
373     else if (strcmp(s, "white")==0) return P_WHITE;
374     else if (strcmp(s, "red")==0) return P_RED;
375     else if (strcmp(s, "green")==0) return P_GREEN;
376     else if (strcmp(s, "blue")==0) return P_BLUE;
377     else if (strcmp(s, "cyan")==0) return P_CYAN;
378     else if (strcmp(s, "magenta")==0) return P_MAGENTA;
379     else if (strcmp(s, "yellow")==0) return P_YELLOW;
380     else if (strcmp(s, "grayd")==0) return P_GRAYD;
381     else if (strcmp(s, "grayc")==0) return P_GRAYC;
382     else if (strcmp(s, "grayb")==0) return P_GRAYB;
383     else if (strcmp(s, "graya")==0) return P_GRAYA;
384     YError("unrecognized color keyword (fg, bg, or 8 primaries only)");
385     return 0;
386   } else {
387     Dimension *dims;
388     int *color = YGet_I(stack, 0, &dims);
389     if (dims && (dims->next || dims->number!=3))
390       YError("color must be integer scalar or triple (rgb)");
391     if (dims) return P_RGB(color[0],color[1],color[2]);
392     if (color[0] < 256) return (color[0]&0xff); /* indexed color */
393     return ((color[0]&0xffffff) | 0x01000000);  /* reform packed RGB color */
394   }
395 }
396 
GetFont(Symbol * stack)397 static int GetFont(Symbol *stack)
398 {
399   Operand op;
400   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
401   stack->ops->FormOperand(stack, &op);
402   if (op.ops==&stringOps) {
403     char *s= ((char **)op.value)[0];
404     if (op.type.dims) YError("illegal font -- need scalar string");
405     if (strncmp(s, "courier", 7)==0)
406       return T_COURIER | GetTypeface(&s[7]);
407     else if (strncmp(s, "times", 5)==0)
408       return T_TIMES | GetTypeface(&s[5]);
409     else if (strncmp(s, "helvetica", 9)==0)
410       return T_HELVETICA | GetTypeface(&s[9]);
411     else if (strncmp(s, "symbol", 6)==0)
412       return T_SYMBOL | GetTypeface(&s[6]);
413     else if (strncmp(s, "schoolbook", 10)==0)
414       return T_NEWCENTURY | GetTypeface(&s[10]);
415     YError("unrecognized font keyword");
416     return 0;
417   } else {
418     return (int)YGetInteger(stack);
419   }
420 }
421 
GetTypeface(char * s)422 static int GetTypeface(char *s)
423 {
424   int face= 0;
425   while (*s) {
426     if (*s=='B' && !(face&T_BOLD)) face|= T_BOLD;
427     else if (*s=='I' && !(face&T_ITALIC)) face|= T_ITALIC;
428     else YError("illegal font keyword suffix -- B is bold, I is italic");
429     s++;
430   }
431   return face;
432 }
433 
GetJustify(Symbol * stack)434 static void GetJustify(Symbol *stack)
435 {
436   Operand op;
437   if (!stack->ops) YError("unexpected keyword argument (BUG?)");
438   stack->ops->FormOperand(stack, &op);
439   if (op.ops==&stringOps) {
440     char *s= ((char **)op.value)[0];
441     if (op.type.dims) YError("illegal justify -- need scalar string");
442     if (*s=='N') { gistA.t.alignH= TH_NORMAL; s++; }
443     else if (*s=='L') { gistA.t.alignH= TH_LEFT; s++; }
444     else if (*s=='C') { gistA.t.alignH= TH_CENTER; s++; }
445     else if (*s=='R') { gistA.t.alignH= TH_RIGHT; s++; }
446     else { while (*s) s++; }
447     if (*s=='N') gistA.t.alignV= TV_NORMAL;
448     else if (*s=='T') gistA.t.alignV= TV_TOP;
449     else if (*s=='C') gistA.t.alignV= TV_CAP;
450     else if (*s=='H') gistA.t.alignV= TV_HALF;
451     else if (*s=='A') gistA.t.alignV= TV_BASE;
452     else if (*s=='B') gistA.t.alignV= TV_BOTTOM;
453     else YError("unrecognized justify keyword");
454   } else {
455     int justify= (int)YGetInteger(stack);
456     gistA.t.alignH= justify&3;
457     gistA.t.alignV= justify>>2;
458   }
459 }
460 
461 /*--------------------------------------------------------------------------*/
462 
463 #undef N_KEYWORDS
464 #define N_KEYWORDS 19
465 static char *plgKeys[N_KEYWORDS+1]= {
466   "legend", "hide", "color", "type", "width",
467   "marks", "mcolor", "marker", "msize", "mspace", "mphase",
468   "rays", "arrowl", "arroww", "rspace", "rphase",
469   "closed", "smooth", "n", 0 };
470 
Y_plg(int nArgs)471 void Y_plg(int nArgs)
472 {
473   Symbol *keySymbols[N_KEYWORDS];
474   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, plgKeys, keySymbols);
475   int iPass= 0;
476   double *x= 0, *y= 0;
477   char *x_name= 0, *y_name= 0;
478   long n= 0;
479   long nparts= 0, *np= 0;
480   int defaultX= 0;
481 
482   if (!CalledAsSubroutine())
483     YError("plg may not be invoked as a function -- subroutine only");
484 
485   while (stack<=sp) {
486     if (!stack->ops) { stack+= 2; continue; }
487 
488     if (iPass==0) y= Get1Ddouble(stack, &n);
489     else if (iPass==1) y_name= YGetString(stack);
490     else if (iPass==2) x= Get1Ddouble(stack, &n);
491     else if (iPass==3) x_name= YGetString(stack);
492     else YError("plg takes at most two non-keyword arguments");
493 
494     iPass++;
495     stack++;
496   }
497 
498   if (!y) YError("plg needs at least one non-keyword argument");
499 
500   /* set legend and hide in gistD */
501   CheckDefaultWindow();
502   LegendAndHide("\001: plg, ", y_name, x_name, (char *)0, (char *)0,
503                 keySymbols);
504 
505   /* set properties, starting from defaults for decorated polylines */
506   GhGetLines();
507 
508   if (YNotNil(keySymbols[2]))
509     gistA.l.color= gistA.m.color= YgetColor(keySymbols[2]);
510   if (YNotNil(keySymbols[3]))
511     gistA.l.type= GetLineType(keySymbols[3]);
512   if (YNotNil(keySymbols[4]))
513     gistA.l.width= YGetReal(keySymbols[4]);
514   if (YNotNil(keySymbols[5]))
515     gistA.dl.marks= (YGetInteger(keySymbols[5])!=0);
516   if (YNotNil(keySymbols[6]))
517     gistA.m.color= YgetColor(keySymbols[6]);
518   if (YNotNil(keySymbols[7]))
519     gistA.m.type= (int)YGetInteger(keySymbols[7]);
520   if (YNotNil(keySymbols[8]))
521     gistA.m.size= YGetReal(keySymbols[8]);
522   if (YNotNil(keySymbols[9]))
523     gistA.dl.mSpace= YGetReal(keySymbols[9]);
524   if (YNotNil(keySymbols[10]))
525     gistA.dl.mPhase= YGetReal(keySymbols[10]);
526   if (YNotNil(keySymbols[11]))
527     gistA.dl.rays= (YGetInteger(keySymbols[11])!=0);
528   if (YNotNil(keySymbols[12]))
529     gistA.dl.arrowL= YGetReal(keySymbols[12]);
530   if (YNotNil(keySymbols[13]))
531     gistA.dl.arrowW= YGetReal(keySymbols[13]);
532   if (YNotNil(keySymbols[14]))
533     gistA.dl.rSpace= YGetReal(keySymbols[14]);
534   if (YNotNil(keySymbols[15]))
535     gistA.dl.rPhase= YGetReal(keySymbols[15]);
536   if (YNotNil(keySymbols[16]))
537     gistA.dl.closed= (YGetInteger(keySymbols[16])!=0);
538   if (YNotNil(keySymbols[17]))
539     gistA.dl.smooth= (YGetInteger(keySymbols[17])!=0);
540 
541   if (YNotNil(keySymbols[18])) {
542     long i, ntot;
543     Dimension *dims;
544     np= YGet_L(keySymbols[18], 0, &dims);
545     if (!dims || dims->next) YError("n= keyword must be 1D in plg");
546     nparts= dims->number;
547     for (i=ntot=0 ; i<nparts ; i++) ntot+= np[i];
548     if (ntot!=n) YError("n= keyword must sum to numberof(y) in plg");
549   }
550 
551   if (!x) {
552     /* default x runs from origin of y dimension in steps of 1 */
553     long i;
554     Array *array=
555       PushDataBlock(NewArray(&doubleStruct,
556                              NewDimension(n, 1L, (Dimension *)0)));
557     array->type.dims->references--;
558     x= array->value.d;
559     defaultX= 1;
560     if (!nparts) {
561       for (i=0 ; i<n ; i++) x[i]= yOrigin+(double)i;
562     } else {
563       long j;
564       for (j=0 ; j<nparts ; j++)
565         for (i=0 ; i<np[j] ; i++) x[i]= yOrigin+(double)i;
566     }
567   }
568 
569   /* add the graph(s) to the current display list */
570   if (!nparts) {
571     curElement= -1;
572     curElement= GdLines(n, x, y);
573   } else {
574     int ce= 0, cel= 0;
575     curElement= -1;
576     while (nparts--) {
577       cel= GdLines(np[0], x, y);
578       if (cel<0) ce= cel;
579       x+= np[0];
580       y+= np[0];
581       np++;
582     }
583     if (!ce) curElement= cel;
584   }
585   if (curElement<0) YWarning("Gist GdLines plotter failed");
586 
587   Drop(nArgs+defaultX);
588 }
589 
590 #undef N_KEYWORDS
591 #define N_KEYWORDS 8
592 static char *plmKeys[N_KEYWORDS+1]= {
593   "legend", "hide", "color", "type", "width", "region", "boundary",
594   "inhibit", 0 };
595 
Y_plm(int nArgs)596 void Y_plm(int nArgs)
597 {
598   Symbol *keySymbols[N_KEYWORDS];
599   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, plmKeys, keySymbols);
600   char *y_name= 0, *x_name= 0, *r_name= 0;
601   GaQuadMesh mesh;
602 
603   if (!CalledAsSubroutine())
604     YError("plm may not be invoked as a function -- subroutine only");
605 
606   stack= GrabMesh(stack, (Symbol *)0, &mesh, &y_name, &x_name, &r_name, 0);
607   while (stack<=sp) {
608     if (!stack->ops) stack+= 2;
609     else YError("plm takes at most three non-keyword arguments");
610   }
611 
612   /* set legend and hide in gistD */
613   CheckDefaultWindow();
614   LegendAndHide("plm, ", y_name, x_name, r_name, (char *)0, keySymbols);
615 
616   /* set properties, starting from defaults for meshes */
617   GhGetMesh();
618   gistD.region= 0;
619   gistD.boundary= 0;
620   gistD.inhibit= 0;
621 
622   if (YNotNil(keySymbols[2]))
623     gistA.l.color= YgetColor(keySymbols[2]);
624   if (YNotNil(keySymbols[3]))
625     gistA.l.type= GetLineType(keySymbols[3]);
626   if (YNotNil(keySymbols[4]))
627     gistA.l.width= YGetReal(keySymbols[4]);
628   if (YNotNil(keySymbols[5]))
629     gistD.region= (int)YGetInteger(keySymbols[5]);
630   if (YNotNil(keySymbols[6]))
631     gistD.boundary= (YGetInteger(keySymbols[6])!=0);
632   if (YNotNil(keySymbols[7]))
633     gistD.inhibit= (int)YGetInteger(keySymbols[7]);
634 
635   curElement= -1;
636   curElement= GdMesh(NOCOPY_MESH, &mesh, gistD.region, gistD.boundary,
637                      gistD.inhibit);
638   if (curElement<0) YWarning("Gist GdMesh plotter failed");
639 
640   RefMesh(&mesh);
641   Drop(nArgs);
642 }
643 
644 #undef N_KEYWORDS
645 #define N_KEYWORDS 1
646 static char *meshKeys[N_KEYWORDS+1]= { "triangle", 0 };
647 
Y_plmesh(int nArgs)648 void Y_plmesh(int nArgs)
649 {
650   Symbol *keySymbols[N_KEYWORDS];
651   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, meshKeys, keySymbols);
652   GaQuadMesh mesh;
653 
654   if (nArgs==0) {
655     Safe_free(&xMesh);
656     Safe_free(&yMesh);
657     Safe_free(&regMesh);
658     Safe_free(&triangleMesh);
659   }
660 
661   stack= GrabMesh(stack, keySymbols[0], &mesh,
662                   (char **)0, (char **)0, (char **)0, 0);
663 
664   if (mesh.x!=xMesh) {
665     /* a new default mesh has been defined */
666     Safe_free(&xMesh);
667     Safe_free(&yMesh);
668     Safe_free(&regMesh);
669     Safe_free(&triangleMesh);
670     RefMesh(&mesh);
671     iMesh= mesh.iMax;
672     jMesh= mesh.jMax;
673     xMesh= mesh.x;
674     yMesh= mesh.y;
675     if (mesh.reg) {
676       regMesh= mesh.reg;
677     } else {
678       /* supply a default region array now */
679       long ijMax= iMesh*jMesh;
680       long i= ijMax+iMesh+1;
681       Array *array=
682         PushDataBlock(NewArray(&intStruct,
683                                NewDimension(i, 1L, (Dimension *)0)));
684       int *r= array->value.i;
685       array->type.dims->references--;
686 
687       for (i=0 ; i<=iMesh ; i++) r[i]= 0;
688       for (i=iMesh+1 ; i<ijMax ; i++) r[i]= 1;
689       for (i=0 ; i<=iMesh ; i++) r[ijMax+i]= 0;
690       for (i=2*iMesh ; i<ijMax ; i+=iMesh) r[i]= 0;
691 
692       array->references++;  /* preserve across Drop */
693       regMesh= array->value.i;
694       Drop(1);
695     }
696     triangleMesh= mesh.triangle;
697 
698   } else {
699     /* perhaps reg or triangle has been updated */
700     if (mesh.x==xMesh) {
701       mesh.x= mesh.y= 0;
702       if (mesh.reg==regMesh) mesh.reg= 0;
703       if (mesh.triangle==triangleMesh) mesh.triangle= 0;
704     }
705     RefMesh(&mesh);
706     if (mesh.reg) {
707       Safe_free(&regMesh);
708       regMesh= mesh.reg;
709     }
710     if (mesh.triangle) {
711       Safe_free(&triangleMesh);
712       triangleMesh= mesh.triangle;
713     }
714   }
715 
716   Drop(nArgs);
717 }
718 
Safe_free(void * vptr)719 static void Safe_free(void *vptr)
720 {
721   void **ptr= vptr;
722   void *obj= *ptr;
723   *ptr= 0;  /* zero reference before freeing object */
724   FreeReference(obj);
725 }
726 
GrabMesh(Symbol * stack,Symbol * triKey,GaQuadMesh * mesh,char ** y_name,char ** x_name,char ** r_name,int tmp)727 static Symbol *GrabMesh(Symbol *stack, Symbol *triKey, GaQuadMesh *mesh,
728                         char **y_name, char **x_name, char **r_name, int tmp)
729 {
730   Symbol *yStack= 0, *xStack= 0, *rStack= 0;
731   int stackInc= y_name? 1 : 2;
732   int iPass= 0;
733 
734   mesh->x= mesh->y= 0;
735   mesh->reg= 0;
736   mesh->iMax= mesh->jMax= 0;
737   mesh->triangle= 0;
738 
739   while (stack<=sp) {
740     if (!stack->ops) { stack+= 2; continue; }
741 
742     if (iPass==0)
743       mesh->y= Get2Ddouble(yStack= stack, &mesh->jMax, &mesh->iMax);
744     else if (iPass==1) *y_name= YGetString(stack);
745     else if (iPass==2)
746       mesh->x= Get2Ddouble(xStack= stack, &mesh->jMax, &mesh->iMax);
747     else if (iPass==3) *x_name= YGetString(stack);
748     else if (iPass==4)
749       mesh->reg= Get2Dint(rStack= stack, &mesh->jMax, &mesh->iMax);
750     else if (iPass==5) *r_name= YGetString(stack);
751     else break;
752 
753     iPass+= stackInc;
754     stack++;
755   }
756 
757   if (YNotNil(triKey))
758     mesh->triangle= Get2Dshort(triKey, &mesh->jMax, &mesh->iMax);
759 
760   if ((mesh->x!=0)^(mesh->y!=0))
761     YError("both y and x arrays must be specified for a mesh");
762 
763   if (!mesh->x) {
764     /* neither y nor x have been specified -- use defaults */
765     if (!xMesh)
766       YError("no default mesh exists to define y and x -- use plmesh");
767     if ((mesh->reg || mesh->triangle) &&
768         (iMesh!=mesh->iMax || jMesh!=mesh->jMax))
769       YError("ireg and triangle must have same dimensions as default mesh");
770     mesh->iMax= iMesh;
771     mesh->jMax= jMesh;
772     mesh->x= xMesh;
773     mesh->y= yMesh;
774 
775   } else {
776     /* both y and x have been specified -- copy them for Gist */
777     if (mesh->iMax<2 || mesh->jMax<2)
778       YError("a mesh have dimensions of at least 2-by-2");
779     mesh->x=
780       CopyArray(xStack, mesh->x, &doubleStruct, mesh->iMax, mesh->jMax);
781     mesh->y=
782       CopyArray(yStack, mesh->y, &doubleStruct, mesh->iMax, mesh->jMax);
783   }
784 
785   /* the Gist region array requires guard zones beyond iMax*jMax */
786   if (mesh->reg) mesh->reg=
787     PadRegionArray(rStack, mesh->reg, mesh->iMax, mesh->jMax);
788   else if (mesh->x==xMesh)
789     mesh->reg= regMesh;
790 
791   if (mesh->triangle) {
792     if (!tmp)
793       mesh->triangle= CopyArray(triKey, mesh->triangle, &shortStruct,
794                                 mesh->iMax, mesh->jMax);
795   } else if (mesh->x==xMesh && triKey) {
796     mesh->triangle= triangleMesh;
797   }
798 
799   return stack;
800 }
801 
RefMesh(GaQuadMesh * mesh)802 static void RefMesh(GaQuadMesh *mesh)
803 {
804   if (mesh->x) ((Array *)Pointee(mesh->x))->references++;
805   if (mesh->y) ((Array *)Pointee(mesh->y))->references++;
806   if (mesh->reg) ((Array *)Pointee(mesh->reg))->references++;
807   if (mesh->triangle) ((Array *)Pointee(mesh->triangle))->references++;
808 }
809 
PadRegionArray(Symbol * stack,int * reg,long iMax,long jMax)810 static int *PadRegionArray(Symbol *stack, int *reg, long iMax, long jMax)
811 {
812   long ijMax= iMax*jMax;
813   long i= ijMax+iMax+1;
814   Array *array=
815     PushDataBlock(NewArray(&intStruct, NewDimension(i, 1L, (Dimension *)0)));
816   int *r= array->value.i;
817   array->type.dims->references--;
818 
819   for (i=0 ; i<=iMax ; i++) r[i]= 0;
820   for (i=iMax+1 ; i<ijMax ; i++) r[i]= reg[i];
821   for (i=0 ; i<=iMax ; i++) r[ijMax+i]= 0;
822   for (i=2*iMax ; i<ijMax ; i+=iMax) r[i]= 0;
823 
824   PopTo(stack);
825   return r;
826 }
827 
CopyArray(Symbol * stack,void * xOld,StructDef * base,long iMax,long jMax)828 static void *CopyArray(Symbol *stack, void *xOld,
829                        StructDef *base, long iMax, long jMax)
830 {
831   if (stack->ops==&dataBlockSym &&
832       (stack->value.db->references || !stack->value.db->ops->isArray)) {
833     long len= iMax*jMax;
834     Array *array=
835       PushDataBlock(NewArray(base, NewDimension(len, 1L, (Dimension *)0)));
836     void *x= array->value.c;
837     array->type.dims->references--;
838     base->Copy(base, x, xOld, len);
839     PopTo(stack);
840     return x;
841   } else {
842     /* no need to copy temporaries */
843     return xOld;
844   }
845 }
846 
847 #undef N_KEYWORDS
848 #define N_KEYWORDS 15
849 static char *plcKeys[N_KEYWORDS+1]= {
850   "legend", "hide", "region", "color", "type", "width",
851   "marks", "mcolor", "marker", "msize", "mspace", "mphase",
852   "smooth", "triangle", "levs", 0 };
853 
854 static double *tmpLevels= 0;
855 
Y_plc(int nArgs)856 void Y_plc(int nArgs)
857 {
858   Symbol *keySymbols[N_KEYWORDS];
859   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, plcKeys, keySymbols);
860   char *z_name= 0, *y_name= 0, *x_name= 0, *r_name= 0;
861   long iMax= 0, jMax= 0, nLevels= 0;
862   double *z= 0, *levels= 0;
863   GaQuadMesh mesh;
864 
865   if (!CalledAsSubroutine())
866     YError("plc may not be invoked as a function -- subroutine only");
867 
868   if (stack<sp) {
869     z= Get2Ddouble(stack++, &jMax, &iMax);
870     z_name= YGetString(stack++);
871   }
872   if (!z) YError("plc needs at least one non-keyword argument");
873   stack= GrabMesh(stack, keySymbols[13], &mesh, &y_name, &x_name, &r_name, 0);
874   while (stack<=sp) {
875     if (!stack->ops) stack+= 2;
876     else YError("plc takes at most four non-keyword arguments");
877   }
878   if (mesh.iMax!=iMax || mesh.jMax!=jMax)
879     YError("z array must have same dimensions as mesh in plc");
880 
881   /* set legend and hide in gistD */
882   CheckDefaultWindow();
883   LegendAndHide("\001: plc, ", z_name, y_name, x_name, r_name, keySymbols);
884 
885   /* set properties, starting from defaults for decorated polylines */
886   GhGetLines();
887   gistD.region= 0;
888 
889   if (YNotNil(keySymbols[2]))
890     gistD.region= (int)YGetInteger(keySymbols[2]);
891   if (YNotNil(keySymbols[3]))
892     gistA.l.color= gistA.m.color= YgetColor(keySymbols[3]);
893   if (YNotNil(keySymbols[4]))
894     gistA.l.type= GetLineType(keySymbols[4]);
895   if (YNotNil(keySymbols[5]))
896     gistA.l.width= YGetReal(keySymbols[5]);
897   if (YNotNil(keySymbols[6]))
898     gistA.dl.marks= (YGetInteger(keySymbols[6])!=0);
899   if (YNotNil(keySymbols[7]))
900     gistA.m.color= YgetColor(keySymbols[7]);
901   if (YNotNil(keySymbols[8]))
902     gistA.m.type= (int)YGetInteger(keySymbols[8]);
903   if (YNotNil(keySymbols[9]))
904     gistA.m.size= YGetReal(keySymbols[9]);
905   if (YNotNil(keySymbols[10]))
906     gistA.dl.mSpace= YGetReal(keySymbols[10]);
907   if (YNotNil(keySymbols[11]))
908     gistA.dl.mPhase= YGetReal(keySymbols[11]);
909   if (YNotNil(keySymbols[12]))
910     gistA.dl.smooth= (YGetInteger(keySymbols[12])!=0);
911 
912   /* set contour levels */
913   if (YNotNil(keySymbols[14])) {
914     levels= Get1Ddouble(keySymbols[14], &nLevels);
915     if (levels)
916       levels= CopyLevels(levels, nLevels);
917   }
918 
919   if (!levels) {
920     /* create a default set of contour levels now */
921     int i;
922     double zmin, zmax, step;
923 
924     nLevels= 8;
925     levels= CopyLevels((double *)0, nLevels);
926     GetPCrange(&zmin, &zmax, z, mesh.reg, gistD.region, iMax, jMax);
927 
928     step= (zmax-zmin)/8.0;
929     levels[0]= zmin+0.5*step;
930     for (i=1 ; i<nLevels ; i++) levels[i]= levels[i-1]+step;
931   }
932 
933   curElement= -1;
934   curElement=
935     GdContours(NOCOPY_MESH, &mesh, gistD.region, z, levels, (int)nLevels);
936   if (curElement<0) YWarning("Gist GdContour plotter failed");
937   tmpLevels= 0;  /* Gist now owns this pointer */
938 
939   RefMesh(&mesh);
940   Drop(nArgs);
941 }
942 
CopyLevels(double * levels,long nLevels)943 static double *CopyLevels(double *levels, long nLevels)
944 {
945   long i;
946   double *tmp= tmpLevels;
947   tmpLevels= 0;
948   if (tmp) p_free(tmp);
949   tmpLevels= p_malloc(sizeof(double)*nLevels);
950   for (i=0 ; i<nLevels ; i++) tmpLevels[i]= levels? levels[i] : 0.0;
951   return tmpLevels;
952 }
953 
GetZCrange(double * zmn,double * zmx,double * z,int * reg,int region,long iMax,long jMax,int zCompressed)954 static void GetZCrange(double *zmn, double *zmx, double *z, int *reg,
955                        int region, long iMax, long jMax, int zCompressed)
956 {
957   double zmin= 0.0, zmax= 0.0;
958   long i, j= iMax-1;
959   long len= (zCompressed? j : iMax)*(jMax-1);
960 
961   if (zCompressed) {
962     long len= (iMax-1)*(jMax-1);
963     if (reg) reg+= iMax+1;
964     for (i=0 ; i<len ; i++) {   /* first loop finds first z */
965       if (reg? (region? (*reg==region) : (*reg!=0)) : 1) {
966         zmin= zmax= z[i];
967         break;
968       }
969       if (reg) {
970         if (!(--j)) { reg+= 2; j= iMax-1; }
971         else reg++;
972       }
973     }
974     if (reg) {
975       if (!(--j)) { reg+= 2; j= iMax-1; }
976       else reg++;
977     }
978     for (i++ ; i<len ; i++) {   /* second loop judges extreme values */
979       if (reg? (region? (*reg==region) : (*reg!=0)) : 1) {
980         if (zmin>z[i]) zmin= z[i];
981         else if (zmax<z[i]) zmax= z[i];
982       }
983       if (reg) {
984         if (!(--j)) { reg+= 2; j= iMax-1; }
985         else reg++;
986       }
987     }
988 
989   } else {
990     z+= iMax+1;                 /* GrabMesh guarantees at least 2-by-2 */
991     if (reg) reg+= iMax+1;
992     for (i=1 ; i<len ; i++) {   /* first loop finds first z */
993       if (--j) {
994         if (reg? (region? (*reg==region) : (*reg!=0)) : 1) {
995           zmin= zmax= z[i];
996           break;
997         }
998       } else {
999         j= iMax;
1000       }
1001     }
1002     for (i++ ; i<len ; i++) {   /* second loop judges extreme values */
1003       if (--j) {
1004         if (reg? (region? (*reg==region) : (*reg!=0)) : 1) {
1005           if (zmin>z[i]) zmin= z[i];
1006           else if (zmax<z[i]) zmax= z[i];
1007         }
1008       } else {
1009         j= iMax;
1010       }
1011     }
1012   }
1013 
1014   *zmn= zmin;
1015   *zmx= zmax;
1016 }
1017 
GetPCrange(double * zmn,double * zmx,double * z,int * reg,int region,long iMax,long jMax)1018 static void GetPCrange(double *zmn, double *zmx, double *z, int *reg,
1019                        int region, long iMax, long jMax)
1020 {
1021   double zmin= 0.0, zmax= 0.0;
1022   long i, len= iMax*jMax;
1023 
1024   for (i=0 ; i<len ; i++) {     /* first loop finds first z */
1025     if (reg? (region?
1026               (reg[i]==region || reg[i+1]==region ||
1027                reg[i+iMax]==region || reg[i+iMax+1]==region) :
1028               (reg[i] || reg[i+1] || reg[i+iMax] || reg[i+iMax+1])) : 1) {
1029       zmin= zmax= z[i];
1030       break;
1031     }
1032   }
1033 
1034   for ( ; i<len ; i++) {        /* second loop judges extreme values */
1035     if (reg? (region?
1036               (reg[i]==region || reg[i+1]==region ||
1037                reg[i+iMax]==region || reg[i+iMax+1]==region) :
1038               (reg[i] || reg[i+1] || reg[i+iMax] || reg[i+iMax+1])) : 1) {
1039       if (zmin>z[i]) zmin= z[i];
1040       else if (zmax<z[i]) zmax= z[i];
1041     }
1042   }
1043 
1044   *zmn= zmin;
1045   *zmx= zmax;
1046 }
1047 
1048 #undef N_KEYWORDS
1049 #define N_KEYWORDS 8
1050 static char *plvKeys[N_KEYWORDS+1]= {
1051   "legend", "hide", "region",
1052   "color", "hollow", "width", "aspect", "scale", 0 };
1053 
Y_plv(int nArgs)1054 void Y_plv(int nArgs)
1055 {
1056   Symbol *keySymbols[N_KEYWORDS];
1057   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, plvKeys, keySymbols);
1058   char *v_name= 0, *u_name= 0, *y_name= 0, *x_name= 0, *r_name= 0;
1059   long iMax= 0, jMax= 0;
1060   double *u= 0, *v= 0, scale;
1061   GaQuadMesh mesh;
1062   int iPass= 0;
1063 
1064   if (!CalledAsSubroutine())
1065     YError("plv may not be invoked as a function -- subroutine only");
1066 
1067   while (stack<=sp) {
1068     if (!stack->ops) { stack+= 2; continue; }
1069     if (iPass==0) v= Get2Ddouble(stack, &jMax, &iMax);
1070     else if (iPass==1) v_name= YGetString(stack);
1071     else if (iPass==2) u= Get2Ddouble(stack, &jMax, &iMax);
1072     else if (iPass==3) u_name= YGetString(stack);
1073     else break;
1074     iPass++;
1075     stack++;
1076   }
1077   if (!u || !v) YError("plv needs at least two non-keyword arguments");
1078   stack= GrabMesh(stack, (Symbol *)0, &mesh, &y_name, &x_name, &r_name, 0);
1079   while (stack<=sp) {
1080     if (!stack->ops) stack+= 2;
1081     else YError("plv takes at most five non-keyword arguments");
1082   }
1083   if (mesh.iMax!=iMax || mesh.jMax!=jMax)
1084     YError("v and u arrays must have same dimensions as mesh in plv");
1085 
1086   /* set legend and hide in gistD */
1087   CheckDefaultWindow();
1088   LegendAndHide("plv, ", v_name, u_name, y_name, x_name, keySymbols);
1089 
1090   /* set properties, starting from defaults for vectors */
1091   GhGetVectors();
1092   gistD.region= 0;
1093 
1094   if (YNotNil(keySymbols[2]))
1095     gistD.region= (int)YGetInteger(keySymbols[2]);
1096   if (YNotNil(keySymbols[3]))
1097     gistA.l.color= gistA.f.color= YgetColor(keySymbols[3]);
1098   if (YNotNil(keySymbols[4]))
1099     gistA.vect.hollow= (YGetInteger(keySymbols[4])!=0);
1100   if (YNotNil(keySymbols[5]))
1101     gistA.l.width= YGetReal(keySymbols[5]);
1102   if (YNotNil(keySymbols[6]))
1103     gistA.vect.aspect= YGetReal(keySymbols[6]);
1104 
1105   /* set vector scale factor */
1106   if (YNotNil(keySymbols[7])) {
1107     scale= YGetReal(keySymbols[7]);
1108 
1109   } else {
1110     /* set vector scale factor to make maximum vector length a
1111        "typical" zone dimension */
1112     double umin, umax, vmin, vmax, xmin, xmax, ymin, ymax;
1113 
1114     GetPCrange(&xmin, &xmax, mesh.x, mesh.reg, gistD.region, iMax, jMax);
1115     GetPCrange(&ymin, &ymax, mesh.y, mesh.reg, gistD.region, iMax, jMax);
1116     GetPCrange(&umin, &umax, u, mesh.reg, gistD.region, iMax, jMax);
1117     GetPCrange(&vmin, &vmax, v, mesh.reg, gistD.region, iMax, jMax);
1118 
1119     umax-= umin;
1120     vmax-= vmin;
1121     if (vmax>umax) umax= vmax;
1122     xmax= (xmax-xmin)+(ymax-ymin);
1123     xmax/= (iMax+jMax);
1124 
1125     if (umax>0.0) scale= xmax/umax;
1126     else scale= 1.0;
1127   }
1128 
1129   curElement= -1;
1130   curElement= GdVectors(NOCOPY_MESH, &mesh, gistD.region, u, v, scale);
1131   if (curElement<0) YWarning("Gist GdVectors plotter failed");
1132 
1133   RefMesh(&mesh);
1134   Drop(nArgs);
1135 }
1136 
1137 #undef N_KEYWORDS
1138 #define N_KEYWORDS 9
1139 static char *plfKeys[N_KEYWORDS+1]= {
1140   "legend", "hide", "region", "top", "cmin", "cmax",
1141   "edges", "ecolor", "ewidth", 0 };
1142 
Y_plf(int nArgs)1143 void Y_plf(int nArgs)
1144 {
1145   Symbol *keySymbols[N_KEYWORDS];
1146   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, plfKeys, keySymbols);
1147   char *z_name= 0, *y_name= 0, *x_name= 0, *r_name= 0;
1148   long iMax= 0, jMax= 0;
1149   double *z= 0;
1150   GpColor *zc= 0;
1151   GaQuadMesh mesh;
1152   int convertedZ= 0;
1153   int rgb = 0;
1154 
1155   if (!CalledAsSubroutine())
1156     YError("plf may not be invoked as a function -- subroutine only");
1157 
1158   if (stack<sp) {
1159     Operand op;
1160     if (stack->ops==&referenceSym) ReplaceRef(stack);
1161     stack->ops->FormOperand(stack, &op);
1162     if (op.ops!=&charOps) {
1163       z= Get2Ddouble(stack, &jMax, &iMax);
1164     } else {
1165       Dimension *next= 0;
1166       if (!op.type.dims || !(next= op.type.dims->next) || next->next) {
1167         if (!next || !next->next || next->next->next ||
1168             next->next->number!=3)
1169           YError("expecting NXxNY or 3xNXxNY array as argument to plf");
1170         rgb = 1;
1171       }
1172       iMax= next->number;
1173       jMax= op.type.dims->number;
1174       zc= op.value;
1175     }
1176     stack++;
1177     z_name= YGetString(stack++);
1178   }
1179   stack= GrabMesh(stack, (Symbol *)0, &mesh, &y_name, &x_name, &r_name, 0);
1180   while (stack<=sp) {
1181     if (!stack->ops) stack+= 2;
1182     else YError("plf takes at most four non-keyword arguments");
1183   }
1184   if ((z || zc) && ((mesh.iMax!=iMax || mesh.jMax!=jMax) &&
1185                     (mesh.iMax!=iMax+1 || mesh.jMax!=jMax+1)))
1186     YError("z array must have same or 1 smaller dimensions as mesh in plf");
1187 
1188   /* set legend and hide in gistD */
1189   CheckDefaultWindow();
1190   CheckDefaultPalette();
1191   LegendAndHide("plf, ", z_name, y_name, x_name, r_name, keySymbols);
1192 
1193   gistD.region= 0;
1194   if (YNotNil(keySymbols[2]))
1195     gistD.region= (int)YGetInteger(keySymbols[2]);
1196 
1197   if (!zc && z) {
1198     /* need to generate colors array on stack now */
1199     double zmin, zmax, scale, offset;
1200 
1201     GrabByteScale(&keySymbols[3], &scale, &offset, &zmin, &zmax,
1202                   z, mesh.reg, gistD.region, mesh.iMax, mesh.jMax,
1203                   mesh.iMax!=iMax);
1204     zc= PushColors(z, iMax*jMax, zmin, zmax, scale, offset);
1205     convertedZ= 1;
1206   }
1207 
1208   GhGetFill();
1209   if (YNotNil(keySymbols[6]))
1210     gistA.e.type= YGetInteger(keySymbols[6])? L_SOLID : L_NONE;
1211   if (YNotNil(keySymbols[7]))
1212     gistA.e.color= YgetColor(keySymbols[7]);
1213   if (YNotNil(keySymbols[8]))
1214     gistA.e.width= YGetReal(keySymbols[8]);
1215   gistA.rgb = rgb;
1216 
1217   if (mesh.iMax==iMax) zc += rgb? 3*(iMax+1) : iMax+1;
1218   curElement= -1;
1219   curElement= GdFillMesh(NOCOPY_MESH, &mesh, gistD.region, zc, iMax);
1220   if (curElement<0) YWarning("Gist GdFillMesh plotter failed");
1221 
1222   RefMesh(&mesh);
1223   Drop(nArgs+convertedZ);
1224 }
1225 
GrabByteScale(Symbol ** keySymbols,double * scale,double * offset,double * zn,double * zx,double * z,int * reg,int region,long iMax,long jMax,int zCompressed)1226 static void GrabByteScale(Symbol **keySymbols, double *scale, double *offset,
1227                           double *zn, double *zx, double *z, int *reg,
1228                           int region, long iMax, long jMax, int zCompressed)
1229 {
1230   int top;
1231   double zmin= 0.0, zmax= 0.0;
1232   int minGiven, maxGiven;
1233   GpColorCell *palette;
1234 
1235   /* get any parameters specified as keywords */
1236   if (YNotNil(keySymbols[0]))
1237     top= (int)YGetInteger(keySymbols[0]);
1238   else
1239     top= GhGetPalette(-1,&palette)-1;
1240   if ((minGiven= YNotNil(keySymbols[1])))
1241     zmin= YGetReal(keySymbols[1]);
1242   if ((maxGiven= YNotNil(keySymbols[2])))
1243     zmax= YGetReal(keySymbols[2]);
1244 
1245   /* fill in zmin and zmax from data if not specified */
1246   if (!minGiven || !maxGiven) {
1247     double zmn, zmx;
1248     GetZCrange(&zmn, &zmx, z, reg, region, iMax, jMax, zCompressed);
1249     if (!minGiven) zmin= zmn;
1250     if (!maxGiven) zmax= zmx;
1251   }
1252 
1253   /* adjust zmin and zmax to avert numerical catastrophes */
1254   if (zmin>zmax) { double tmp= zmin; zmin= zmax; zmax= tmp; }
1255   else if (zmin==zmax) {
1256     if (zmin>0.0) { zmin= 0.9999*zmin; zmax= 1.0001*zmax; }
1257     if (zmin<0.0) { zmin= 1.0001*zmin; zmax= 0.9999*zmax; }
1258     else { zmin= -0.0001; zmax= 0.0001; }
1259   }
1260   *zn= zmin;
1261   *zx= zmax;
1262 
1263   /* adjust top value if it is silly */
1264   if (top<0 || top>255) top= 255;
1265 
1266   /* (byte value)= scale*(z cut off at zmin, zmax)+offset
1267      maps from z to interval [0, top] */
1268   *scale= (double)top/(zmax-zmin);
1269   *offset= zmin-(0.4999/(*scale));        /* zmin->0.5, zmax->top+0.5 */
1270 }
1271 
PushColors(double * z,long len,double zmin,double zmax,double scale,double offset)1272 static GpColor *PushColors(double *z, long len, double zmin, double zmax,
1273                            double scale, double offset)
1274 {
1275   long i;
1276   double zz;
1277   Array *array=
1278     PushDataBlock(NewArray(&charStruct,
1279                            NewDimension(len, 1L, (Dimension *)0)));
1280   GpColor *zc= (GpColor *)array->value.c;
1281   array->type.dims->references--;
1282 
1283   for (i=0 ; i<len ; i++) {
1284     zz= z[i];
1285     if (zz<zmin) zz= zmin;
1286     else if (zz>zmax) zz= zmax;
1287     zc[i]= (int)((zz-offset)*scale);
1288   }
1289 
1290   return zc;
1291 }
1292 
1293 #undef N_KEYWORDS
1294 #define N_KEYWORDS 5
1295 static char *pliKeys[N_KEYWORDS+1]= {
1296   "legend", "hide", "top", "cmin", "cmax", 0 };
1297 
Y_pli(int nArgs)1298 void Y_pli(int nArgs)
1299 {
1300   Symbol *keySymbols[N_KEYWORDS];
1301   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, pliKeys, keySymbols);
1302   char *z_name= 0;
1303   double *z= 0, x0, y0, x1, y1;
1304   long iMax= 0, jMax= 0;
1305   GpColor *zc= 0;
1306   int convertedZ= 0;
1307   int iPass= 0;
1308   int rgb = 0;
1309 
1310   if (!CalledAsSubroutine())
1311     YError("pli may not be invoked as a function -- subroutine only");
1312 
1313   x0= y0= x1= y1= 0.0;
1314   while (stack<=sp) {
1315     if (!stack->ops) { stack+= 2; continue; }
1316     if (iPass==0) {
1317       Operand op;
1318       if (stack->ops==&referenceSym) ReplaceRef(stack);
1319       stack->ops->FormOperand(stack, &op);
1320       if (op.ops!=&charOps) {
1321         z= Get2Ddouble(stack, &jMax, &iMax);
1322       } else {
1323         Dimension *next= 0;
1324         if (!op.type.dims || !(next= op.type.dims->next) || next->next) {
1325           if (!next || !next->next || next->next->next ||
1326               next->next->number!=3)
1327             YError("expecting NXxNY or 3xNXxNY array as argument to pli");
1328           rgb = 1;
1329         }
1330         iMax= next->number;
1331         jMax= op.type.dims->number;
1332         zc= op.value;
1333       }
1334     } else if (iPass==1) z_name= YGetString(stack);
1335     else if (iPass==2) x0= YGetReal(stack);
1336     else if (iPass==3) y0= YGetReal(stack);
1337     else if (iPass==4) x1= YGetReal(stack);
1338     else if (iPass==5) y1= YGetReal(stack);
1339     else YError("pli takes at most five non-keyword arguments");
1340     iPass++;
1341     stack++;
1342   }
1343   if (!z && !zc) YError("pli needs at least one non-keyword argument");
1344 
1345   /* handle defaulted corner values */
1346   if (iPass!=2 && iPass!=4 && iPass!=6)
1347       YError("pli needs either 0, 1, or 2 corner (x,y) points");
1348   if (iPass==2) {
1349     /* no corners specified */
1350     x0= y0= 0.0;
1351     x1= (double)iMax;
1352     y1= (double)jMax;
1353   } else if (iPass==4) {
1354     /* two corners specified */
1355     x1= x0;
1356     y1= y0;
1357     x0= y0= 0.0;
1358   }
1359 
1360   /* set legend and hide in gistD */
1361   CheckDefaultWindow();
1362   CheckDefaultPalette();
1363   LegendAndHide("pli, ", z_name, (char *)0,(char *)0,(char *)0, keySymbols);
1364 
1365   if (!zc) {
1366     /* need to generate colors array on stack now */
1367     double zmin, zmax, scale, offset;
1368 
1369     GrabByteScale(&keySymbols[2], &scale, &offset, &zmin, &zmax,
1370                   z, (int *)0, 0, iMax+1, jMax+1, 1);
1371     zc= PushColors(z, iMax*jMax, zmin, zmax, scale, offset);
1372     convertedZ= 1;
1373   }
1374 
1375   gistA.rgb = rgb;
1376 
1377   curElement= -1;
1378   curElement= GdCells(x0, y0, x1, y1, iMax, jMax, iMax, zc);
1379   if (curElement<0) YWarning("Gist GdCells plotter failed");
1380 
1381   Drop(nArgs+convertedZ);
1382 }
1383 
1384 #undef N_KEYWORDS
1385 #define N_KEYWORDS 8
1386 static char *plfpKeys[N_KEYWORDS+1]= {
1387   "legend", "hide", "top", "cmin", "cmax", "edges", "ecolor", "ewidth", 0 };
1388 
Y_plfp(int nArgs)1389 void Y_plfp(int nArgs)
1390 {
1391   Symbol *keySymbols[N_KEYWORDS];
1392   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, plfpKeys, keySymbols);
1393   long n= 0, ny= 0, *pn= 0;
1394   double *z= 0, *x= 0, *y= 0;
1395   GpColor *zc= 0;
1396   int convertedZ= 0;
1397   int rgb = 0;
1398 
1399   if (!CalledAsSubroutine())
1400     YError("plfp may not be invoked as a function -- subroutine only");
1401 
1402   while (stack<=sp) {
1403     Operand op;
1404     if (!stack->ops) {
1405       stack+= 2;
1406       continue;
1407     }
1408     if (stack->ops==&referenceSym) ReplaceRef(stack);
1409     if (!convertedZ) {
1410       stack->ops->FormOperand(stack, &op);
1411       if (op.ops!=&charOps) {
1412         if (op.ops!=&voidOps)
1413           z= Get1Ddouble(stack, &n);
1414       } else {
1415         Dimension *next = op.type.dims? op.type.dims->next : 0;
1416         if (!op.type.dims || next) {
1417           if (next->next || next->number!=3)
1418             YError("expecting 1D or 3xN color array as argument to plfp");
1419           rgb = 1;
1420         }
1421         n= op.type.number;
1422         if (rgb) n /= 3;
1423         zc= op.value;
1424       }
1425       convertedZ= 1;
1426     } else if (!y) {
1427       y= Get1Ddouble(stack, &ny);
1428       if (!y) YError("expecting non-nil argument in plfp");
1429     } else if (!x) {
1430       long nx= 0;
1431       x= Get1Ddouble(stack, &nx);
1432       if (!x) YError("expecting non-nil argument in plfp");
1433       if (nx!=ny) YError("numberof(x)!=numberof(y) in plfp");
1434     } else if (!pn) {
1435       long i, np;
1436       stack->ops->FormOperand(stack, &op);
1437       if (op.ops==&voidOps) YError("expecting non-nil argument in plfp");
1438       if (op.ops->promoteID>T_LONG || (op.type.dims && op.type.dims->next))
1439         YError("expecting 1D array convertable to type long as argument");
1440       op.ops->ToLong(&op);
1441       if ((z||zc) && op.type.number!=n)
1442         YError("numberof(pn)!=numberof(z) in plfp");
1443       else
1444         n= op.type.number;
1445       pn= (long *)op.value;
1446       for (np=i=0 ; i<n ; i++) np+= pn[i];
1447       if (np!=ny) YError("numberof(y)!=sum(pn) in plfp");
1448     } else {
1449       YError("plfp takes at most four non-keyword arguments");
1450     }
1451     stack++;
1452   }
1453   if (!pn) YError("plfp needs four non-keyword arguments");
1454 
1455   /* set legend and hide in gistD */
1456   CheckDefaultWindow();
1457   CheckDefaultPalette();
1458   /* would need to add plfp to quine list with YpQuine to get legend
1459      LegendAndHide("plfp, ", z_name, y_name, x_name, r_name, keySymbols); */
1460   LegendAndHide((char *)0, (char *)0, (char *)0,
1461                 (char *)0, (char *)0, keySymbols);
1462 
1463   if (!zc && z) {
1464     /* need to generate colors array on stack now */
1465     double zmin, zmax, scale, offset;
1466 
1467     GrabByteScale(&keySymbols[2], &scale, &offset, &zmin, &zmax,
1468                   z, (int *)0, 0, n+1, 2L, 1);
1469     zc= PushColors(z, n, zmin, zmax, scale, offset);
1470     convertedZ= 1;
1471   } else {
1472     convertedZ= 0;
1473   }
1474 
1475   GhGetFill();
1476   if (YNotNil(keySymbols[5]))
1477     gistA.e.type= YGetInteger(keySymbols[5])? L_SOLID : L_NONE;
1478   if (YNotNil(keySymbols[6]))
1479     gistA.e.color= YgetColor(keySymbols[6]);
1480   if (YNotNil(keySymbols[7]))
1481     gistA.e.width= YGetReal(keySymbols[7]);
1482   gistA.rgb = rgb;
1483 
1484   curElement= -1;
1485   curElement= GdFill(n, zc, x, y, pn);
1486   if (curElement<0) YWarning("Gist GdFill plotter failed");
1487 
1488   Drop(nArgs+convertedZ);
1489 }
1490 
1491 #undef N_KEYWORDS
1492 #define N_KEYWORDS 9
1493 static char *pltKeys[N_KEYWORDS+1]= {
1494   "legend", "hide",
1495   "color", "font", "height", "orient", "justify", "opaque", "tosys", 0 };
1496 
Y_plt(int nArgs)1497 void Y_plt(int nArgs)
1498 {
1499   Symbol *keySymbols[N_KEYWORDS];
1500   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, pltKeys, keySymbols);
1501   char *text= 0;
1502   double x= 0.0, y= 0.0;
1503   int toSys;
1504   int iPass= 0;
1505 
1506   while (stack<=sp) {
1507     if (!stack->ops) { stack+= 2; continue; }
1508     if (iPass==0) text= YGetString(stack);
1509     else if (iPass==1) x= YGetReal(stack);
1510     else if (iPass==2) y= YGetReal(stack);
1511     iPass++;
1512     stack++;
1513   }
1514   if (iPass!=3)
1515     YError("plt requires exactly three non-keyword arguments");
1516 
1517   /* set legend and hide in gistD */
1518   CheckDefaultWindow();
1519   LegendAndHide((char *)0, (char *)0, (char *)0,
1520                 (char *)0, (char *)0, keySymbols);
1521 
1522   /* set properties, starting from defaults for vectors */
1523   GhGetText();
1524 
1525   if (YNotNil(keySymbols[2]))
1526     gistA.t.color= YgetColor(keySymbols[2]);
1527   if (YNotNil(keySymbols[3]))
1528     gistA.t.font= GetFont(keySymbols[3]);
1529   if (YNotNil(keySymbols[4]))
1530     gistA.t.height= YGetReal(keySymbols[4])*ONE_POINT;
1531   if (YNotNil(keySymbols[5]))
1532     gistA.t.orient= YGetInteger(keySymbols[5]);
1533   if (YNotNil(keySymbols[6]))
1534     GetJustify(keySymbols[6]);
1535   if (YNotNil(keySymbols[7]))
1536     gistA.t.opaque= (YGetInteger(keySymbols[7])!=0);
1537 
1538   if (!gistA.t.orient) {
1539     gistA.t.orient= TX_RIGHT;
1540   } else {
1541     if (gistA.t.orient==1) gistA.t.orient= TX_UP;
1542     else if (gistA.t.orient==2) gistA.t.orient= TX_LEFT;
1543     else if (gistA.t.orient==3) gistA.t.orient= TX_DOWN;
1544     else {
1545       gistA.t.orient= TX_RIGHT;
1546       YError("orient= keyword must be 0, 1, 2, or 3");
1547     }
1548   }
1549 
1550   toSys= 0;
1551   if (YNotNil(keySymbols[8]))
1552     toSys= (YGetInteger(keySymbols[8])!=0);
1553 
1554   if (!text) text= "";
1555   curElement= -1;
1556   curElement= GdText(x, y, text, toSys);
1557   if (curElement<0) YWarning("Gist GdText plotter failed");
1558 
1559   Drop(nArgs);
1560 }
1561 
1562 #undef N_KEYWORDS
1563 #define N_KEYWORDS 5
1564 static char *pldjKeys[N_KEYWORDS+1]= {
1565   "legend", "hide", "color", "type", "width", 0 };
1566 
Y_pldj(int nArgs)1567 void Y_pldj(int nArgs)
1568 {
1569   Symbol *keySymbols[N_KEYWORDS];
1570   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, pldjKeys, keySymbols);
1571   double *x0= 0, *y0= 0, *x1= 0, *y1= 0;
1572   char *x0_name= 0, *y0_name= 0, *x1_name= 0, *y1_name= 0;
1573   long n= 0;
1574   int iPass= 0;
1575 
1576   if (!CalledAsSubroutine())
1577     YError("pldj may not be invoked as a function -- subroutine only");
1578 
1579   while (stack<=sp) {
1580     if (!stack->ops) { stack+= 2; continue; }
1581     if (iPass==0) x0= GetDouble(stack, &n);
1582     else if (iPass==1) x0_name= YGetString(stack);
1583     else if (iPass==2) y0= GetDouble(stack, &n);
1584     else if (iPass==3) y0_name= YGetString(stack);
1585     else if (iPass==4) x1= GetDouble(stack, &n);
1586     else if (iPass==5) x1_name= YGetString(stack);
1587     else if (iPass==6) y1= GetDouble(stack, &n);
1588     else if (iPass==7) y1_name= YGetString(stack);
1589     iPass++;
1590     stack++;
1591   }
1592   if (iPass!=8)
1593     YError("pldj requires exactly four non-keyword arguments");
1594 
1595   /* set legend and hide in gistD */
1596   CheckDefaultWindow();
1597   LegendAndHide("pldj, ", x0_name, y0_name, x1_name, y1_name, keySymbols);
1598 
1599   /* set properties, starting from defaults for simple polylines */
1600   GhGetMesh();
1601 
1602   if (YNotNil(keySymbols[2]))
1603     gistA.l.color= YgetColor(keySymbols[2]);
1604   if (YNotNil(keySymbols[3]))
1605     gistA.l.type= GetLineType(keySymbols[3]);
1606   if (YNotNil(keySymbols[4]))
1607     gistA.l.width= YGetReal(keySymbols[4]);
1608 
1609   curElement= -1;
1610   curElement= GdDisjoint(n, x0, y0, x1, y1);
1611   if (curElement<0) YWarning("Gist GdDisjoint plotter failed");
1612 
1613   Drop(nArgs);
1614 }
1615 
GetDouble(Symbol * stack,long * n)1616 static double *GetDouble(Symbol *stack, long *n)
1617 {
1618   Operand op;
1619   if (!stack || !stack->ops)
1620     YError("unexpected keyword or missing argument (BUG?)");
1621   stack->ops->FormOperand(stack, &op);
1622   if (op.ops->promoteID>T_DOUBLE)
1623     YError("expecting argument convertable to type double");
1624   op.ops->ToDouble(&op);
1625   *n= op.type.number;
1626   return (double *)op.value;
1627 }
1628 
1629 /*--------------------------------------------------------------------------*/
1630 
1631 #undef N_KEYWORDS
1632 #define N_KEYWORDS 3
1633 static char *limKeys[N_KEYWORDS+1]= {
1634   "square", "nice", "restrict", 0 };
1635 
Y_limits(int nArgs)1636 void Y_limits(int nArgs)
1637 {
1638   /* NB-- If the plot has not been displayed yet, this will not retrieve
1639           the latest extreme values calculated by GdScan.  Nevertheless,
1640           it DOES retrieve the precise state of the limits at the time
1641           of this call, and retoring them will work correctly.  */
1642   Symbol *keySymbols[N_KEYWORDS];
1643   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, limKeys, keySymbols);
1644   Operand op;
1645   double old_limits[5], *new_limits= 0;
1646   double xmin= 0.0, xmax= 0.0, ymin= 0.0, ymax= 0.0;
1647   char *extreme;
1648   int flags= 0, changed= 0;
1649   int iPass= 0;
1650 
1651   while (stack<=sp) {
1652     if (!stack->ops) { stack+= 2; continue; }
1653     if (new_limits)
1654       YError("only one argument allowed when restoring saved limits");
1655 
1656     if (YNotNil(stack)) {
1657       stack->ops->FormOperand(stack, &op);
1658       extreme= (op.ops==&stringOps && !op.type.dims)?
1659         ((char **)op.value)[0] : 0;
1660       if (!extreme || (extreme[0]!='e' && extreme[0]!='E') ||
1661           extreme[1]!='\0') {
1662         if (op.ops->promoteID>T_DOUBLE ||
1663             (op.type.dims && (iPass!=0 || op.type.dims->number!=5)))
1664           YError("illegal argument type in limits function");
1665         op.ops->ToDouble(&op);
1666       }
1667       if (iPass==0) {
1668         if (op.type.dims) new_limits= (double *)op.value;
1669         else if (extreme) flags|= D_XMIN;
1670         else xmin= *((double *)op.value);
1671         changed|= 1;
1672       } else if (iPass==1) {
1673         if (extreme) flags|= D_XMAX;
1674         else xmax= *((double *)op.value);
1675         changed|= 2;
1676       } else if (iPass==2) {
1677         if (extreme) flags|= D_YMIN;
1678         else ymin= *((double *)op.value);
1679         changed|= 4;
1680       } else if (iPass==3) {
1681         if (extreme) flags|= D_YMAX;
1682         else ymax= *((double *)op.value);
1683         changed|= 8;
1684       } else {
1685         YError("limits takes at most 4 non-keyword arguments");
1686       }
1687     }
1688 
1689     iPass++;
1690     stack++;
1691   }
1692 
1693   /* retrieve current limits and flags */
1694   GdGetLimits();
1695   old_limits[0]= gistD.limits.xmin;
1696   old_limits[1]= gistD.limits.xmax;
1697   old_limits[2]= gistD.limits.ymin;
1698   old_limits[3]= gistD.limits.ymax;
1699   old_limits[4]= (double)gistD.flags;
1700 
1701   /* process square=, nice=, restrict= keywords */
1702   if (YNotNil(keySymbols[0])) {
1703     if (new_limits) flags= 1;
1704     else if (YGetInteger(keySymbols[0])) gistD.flags|= D_SQUARE;
1705     else gistD.flags&= ~D_SQUARE;
1706     changed|= 16;
1707   }
1708   if (YNotNil(keySymbols[1])) {
1709     if (new_limits) flags= 1;
1710     else if (YGetInteger(keySymbols[1])) gistD.flags|= D_NICE;
1711     else gistD.flags&= ~D_NICE;
1712     changed|= 16;
1713   }
1714   if (YNotNil(keySymbols[2])) {
1715     if (new_limits) flags= 1;
1716     else if (YGetInteger(keySymbols[2])) gistD.flags|= D_RESTRICT;
1717     else gistD.flags&= ~D_RESTRICT;
1718     changed|= 16;
1719   }
1720 
1721   if (new_limits) {
1722     /* restore limits saved with previous limits command */
1723     if (flags) YError("no keywords allowed when restoring saved limits");
1724     gistD.limits.xmin= new_limits[0];
1725     gistD.limits.xmax= new_limits[1];
1726     gistD.limits.ymin= new_limits[2];
1727     gistD.limits.ymax= new_limits[3];
1728     gistD.flags= (int)new_limits[4];
1729 
1730   } else if (nArgs) {
1731     /* process xmin, xmax, ymin, ymax */
1732     if (changed&1) {
1733       gistD.limits.xmin= xmin;
1734       if (flags&D_XMIN) gistD.flags|= D_XMIN;
1735       else gistD.flags&= ~D_XMIN;
1736     }
1737     if (changed&2) {
1738       gistD.limits.xmax= xmax;
1739       if (flags&D_XMAX) gistD.flags|= D_XMAX;
1740       else gistD.flags&= ~D_XMAX;
1741     }
1742     if (changed&4) {
1743       gistD.limits.ymin= ymin;
1744       if (flags&D_YMIN) gistD.flags|= D_YMIN;
1745       else gistD.flags&= ~D_YMIN;
1746     }
1747     if (changed&8) {
1748       gistD.limits.ymax= ymax;
1749       if (flags&D_YMAX) gistD.flags|= D_YMAX;
1750       else gistD.flags&= ~D_YMAX;
1751     }
1752 
1753   } else {
1754     /* just reset to extreme values */
1755     changed= (D_XMIN | D_XMAX | D_YMIN | D_YMAX);
1756     gistD.flags|= changed;
1757   }
1758 
1759   /* set new limits in drawing */
1760   if (changed) GdSetLimits();
1761 
1762   Drop(nArgs);
1763   if (!CalledAsSubroutine()) {
1764     Array *array=
1765       PushDataBlock(NewArray(&doubleStruct,
1766                              NewDimension(5L, 1L, (Dimension *)0)));
1767     double *lims= array->value.d;
1768     int i;
1769     array->type.dims->references--;
1770     for (i=0 ; i<5 ; i++) lims[i]= old_limits[i];
1771   }
1772 }
1773 
Y_logxy(int nArgs)1774 void Y_logxy(int nArgs)
1775 {
1776   int xflag= 0, yflag= 0, changed;
1777   int iPass= 0;
1778   Symbol *stack= sp-nArgs+1;
1779 
1780   changed= 0;
1781   while (stack<=sp) {
1782     if (!stack->ops) YError("logxy takes no keyword arguments");
1783     if (YNotNil(stack)) {
1784       if (iPass==0) {
1785         xflag= (YGetInteger(stack)!=0);
1786         changed|= 1;
1787       } else if (iPass==1) {
1788         yflag= (YGetInteger(stack)!=0);
1789         changed|= 2;
1790       } else {
1791         YError("logxy takes at most two arguments");
1792       }
1793     }
1794     iPass++;
1795     stack++;
1796   }
1797 
1798   if (changed) {
1799     GdGetLimits();
1800     if (changed&1) {
1801       if (xflag) gistD.flags|= D_LOGX;
1802       else gistD.flags&= ~D_LOGX;
1803     }
1804     if (changed&2) {
1805       if (yflag) gistD.flags|= D_LOGY;
1806       else gistD.flags&= ~D_LOGY;
1807     }
1808     GdSetLimits();
1809   }
1810 
1811   Drop(nArgs);
1812 }
1813 
Y_zoom_factor(int nArgs)1814 void Y_zoom_factor(int nArgs)
1815 {
1816   if (nArgs!=1) YError("zoom_factor takes exactly one argument");
1817   DISPLAY_ZOOM_FACTOR= YGetReal(sp);
1818   /* avert various disasters --
1819      doesn't address DISPLAY_ZOOM_FACTOR==1.0, which would be frustrating... */
1820   if (DISPLAY_ZOOM_FACTOR<0.0) DISPLAY_ZOOM_FACTOR= -DISPLAY_ZOOM_FACTOR;
1821   if (DISPLAY_ZOOM_FACTOR<0.05) DISPLAY_ZOOM_FACTOR= 0.05;
1822   else if (DISPLAY_ZOOM_FACTOR>20.0) DISPLAY_ZOOM_FACTOR= 20.0;
1823 }
1824 
Y_unzoom(int nArgs)1825 void Y_unzoom(int nArgs)
1826 {
1827   if (nArgs!=0) YError("unzoom takes exactly zero argument");
1828   GdRevertLimits(1);
1829 }
1830 
1831 /*--------------------------------------------------------------------------*/
1832 
1833 static char *window_name(int n);
1834 static char *
window_name(int n)1835 window_name(int n)
1836 {
1837   static char buffer[20];
1838   sprintf(buffer, "Yorick %d", n);
1839   return buffer;
1840 }
1841 
SetHCPDefault(void)1842 static void SetHCPDefault(void)
1843 {
1844   int i, j;
1845   p_file *f;
1846   char hcpName[16];
1847   if (!hcpPSdefault) strcpy(hcpName, "Aa00.cgm");
1848   else strcpy(hcpName, "Aa00.ps");
1849 
1850   for (j='A' ; j<='Z' ; j++) {
1851     hcpName[0]= j;
1852     for (i='a' ; i<='z' ; i++) {
1853       hcpName[1]= i;
1854       if ((f= p_fopen(hcpName, "rb"))) p_fclose(f);
1855       else goto got1;
1856     }
1857   }
1858   YError("you appear to have Aa00 through Zz00 hcp files -- clean up");
1859 
1860  got1:
1861   if (!hcpPSdefault)
1862     hcpDefault= GpCGMEngine("Yorick default", 0, hcpDump,
1863                             SetHCPname(-1, hcpName));
1864   else
1865     hcpDefault= GpPSEngine("Yorick default", 0, hcpDump,
1866                            SetHCPname(-1, hcpName));
1867   if (!hcpDefault) YError("failed to create default hcp file");
1868 }
1869 
CheckDefaultWindow(void)1870 static void CheckDefaultWindow(void)
1871 {
1872   int i;
1873   for (i=0 ; i<GH_NDEVS ; i++) if (ghDevices[i].drawing) {
1874     if (!ghDevices[i].display && !ghDevices[i].hcp) {
1875       Drauing *drawing= ghDevices[i].drawing;
1876       ghDevices[i].drawing= 0;
1877       GdKillDrawing(drawing);
1878       curElement= -1;
1879     }
1880   }
1881   if (GhGetPlotter()<0) {
1882     for (i=0 ; i<GH_NDEVS ; i++) if (ghDevices[i].drawing)
1883       YError("graphics window killed -- use window command to re-select");
1884     ghDevices[0].drawing=
1885       GdNewDrawing(defaultStyle? defaultStyle : "work.gs");
1886     curElement= -1;
1887     if (!ghDevices[0].drawing)
1888       YError("failed to create drawing -- Gist work.gs style sheet missing");
1889     ghDevices[0].doLegends= defaultLegends;
1890 
1891 #ifndef NO_XLIB
1892     gist_private_map = gist_rgb_hint = 0;
1893     gx_parent = 0;
1894     ghDevices[0].display=
1895       DISPLAY_ENGINE(window_name(0), 0, defaultDPI, (char *)0);
1896     if (!ghDevices[0].display)
1897       YError("failed to open X display or create X window");
1898 #else
1899     ghDevices[0].display= 0;
1900     ghDevices[0].hcp= hcpDefault;
1901     hcpDefault= 0;
1902 #endif
1903 
1904     GhSetPlotter(0);
1905   }
1906 }
1907 
CheckDefaultPalette(void)1908 static void CheckDefaultPalette(void)
1909 {
1910   GpColorCell *palette;
1911   GhGetPalette(-1, &palette);
1912   if (!palette) GhReadPalette(-1, defaultPalette? defaultPalette : "earth.gp",
1913                               &palette, maxColors);
1914 }
1915 
CheckPalette(void)1916 static void CheckPalette(void)
1917 {
1918   int n= GhGetPlotter();
1919   if (n>=0 && !ghDevices[n].hcp) {
1920     if (!hcpDefault) SetHCPDefault();
1921     SetHCPPalette();
1922   }
1923 }
1924 
SetHCPname(int n,char * name)1925 static char *SetHCPname(int n, char *name)
1926 {
1927   char *now;
1928   if (n<0 || n>GH_NDEVS) n= GH_NDEVS;
1929   now= hcpNames[n];
1930   hcpNames[n]= YExpandName(name);
1931   p_free(now);
1932   return hcpNames[n];
1933 }
1934 
GetHCPname(int n)1935 static char *GetHCPname(int n)
1936 {
1937   if (n>=0 && n<GH_NDEVS && ghDevices[n].hcp) return hcpNames[n];
1938   else return hcpNames[GH_NDEVS];
1939 }
1940 
1941 #undef N_KEYWORDS
1942 #define N_KEYWORDS 14
1943 static char *windowKeys[N_KEYWORDS+1]= {
1944   "display", "dpi", "private", "hcp", "legends", "dump", "style", "wait",
1945   "width", "height", "rgb", "parent", "xpos", "ypos", 0 };
1946 
1947 static Instruction *yg_pc_resume = 0;
1948 extern void yg_got_expose(void);
1949 extern Instruction *ym_suspend(void);
1950 extern void ym_resume(Instruction *);
1951 extern int yg_blocking;
1952 int yg_blocking = 0;
1953 
Y_window(int nArgs)1954 void Y_window(int nArgs)
1955 {
1956   int n, nGiven;
1957   Symbol *keySymbols[N_KEYWORDS];
1958   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, windowKeys, keySymbols);
1959   GpColorCell *palette;
1960   int nColors= 0;
1961   int wait_for_expose = 0;
1962   int rgb = 0;
1963   int n0 = GhGetPlotter();
1964 
1965   if (stack<=sp && YNotNil(stack++)) {
1966     n= (int)YGetInteger(stack-1);
1967     if (n<0 || n>=GH_NDEVS) YError("bad graphics windows are number");
1968     nGiven= (!ghDevices[n].display && !ghDevices[n].hcp);
1969   } else {
1970     n = n0;
1971     nGiven= (n<0);
1972     if (nGiven) n= 0;
1973   }
1974   while (stack<=sp) {
1975     if (!stack->ops) stack+= 2;
1976     else YError("window function takes at most one non-keyword argument");
1977   }
1978 
1979   curElement= -1;
1980 
1981   /* get current palette for this graphics window */
1982   nColors= GhGetPalette(n, &palette);
1983 
1984   /* check for width and height specs, subwindow hack */
1985 #ifndef NO_XLIB
1986   gx_parent = 0;
1987   if (YNotNil(keySymbols[8])) {
1988     extern int gx75width, gx100width;
1989     int width= (int)YGetInteger(keySymbols[8]);
1990     if (width>30) gx75width= gx100width= width;
1991     else { gx75width= 450; gx100width= 600; }
1992   }
1993   if (YNotNil(keySymbols[9])) {
1994     extern int gx75height, gx100height;
1995     int height= (int)YGetInteger(keySymbols[9]);
1996     if (height>30) gx75height= gx100height= height;
1997     else { gx75height= 450; gx100height= 600; }
1998   }
1999   if (YNotNil(keySymbols[11])) {
2000     extern int gx_xloc, gx_yloc;
2001     gx_parent = (unsigned long)YGetInteger(keySymbols[11]);
2002     gx_xloc = gx_yloc = 0;
2003     if (YNotNil(keySymbols[12]))
2004       gx_xloc = (int)YGetInteger(keySymbols[12]);
2005     if (YNotNil(keySymbols[13]))
2006       gx_yloc = (int)YGetInteger(keySymbols[13]);
2007   }
2008 #endif
2009 
2010   if (nGiven || keySymbols[0] || keySymbols[1] || keySymbols[2]) {
2011     /* display= and/or dpi= keywords */
2012     char *display= 0;
2013     int dpi= defaultDPI;
2014     int privmap = 0;
2015     Engine *engine= ghDevices[n].display;  /* current display engine */
2016 
2017     if (YNotNil(keySymbols[0])) display= YGetString(keySymbols[0]);
2018     if (YNotNil(keySymbols[1])) {
2019       if (engine) YError("cannot change dpi of an existing graphics window");
2020       dpi= (int)YGetInteger(keySymbols[1]);
2021       /*if (dpi!=100 && dpi!=75)
2022         YError("dpi=100 or dpi=75 are only legal values");*/
2023       if (dpi<25) dpi = 25;
2024       else if (dpi>2400) dpi = 2400;
2025     }
2026     if (YNotNil(keySymbols[2])) {
2027       /* private= keyword -- turn on/off private X window colormap */
2028       if (engine)
2029         YError("cannot give existing graphics window private colormap");
2030       if (!(nGiven? (!display || display[0]) : (display && display[0])))
2031         YError("private= keyword not legal without display engine");
2032       privmap = YGetInteger(keySymbols[2])!=0;
2033     }
2034     if (YNotNil(keySymbols[10])) {
2035       /* rgb= keyword -- maybe make this a true color window */
2036       if (engine)
2037         YError("cannot use rgb= on existing graphics window");
2038       if (!(nGiven? (!display || display[0]) : (display && display[0])))
2039         YError("rgb= keyword not legal without display engine");
2040       rgb = YGetInteger(keySymbols[10])!=0;
2041     }
2042 
2043     if (engine) GpKillEngine(engine);
2044 
2045     if (nGiven? (!display || display[0]) : (display && display[0])) {
2046 #ifndef NO_XLIB
2047       gist_private_map = privmap;
2048       gist_rgb_hint = rgb;
2049       engine= DISPLAY_ENGINE(window_name(n), 0, dpi, display);
2050       if (!engine) YError("failed to open X display or create X window");
2051       else wait_for_expose = 1;
2052       ghDevices[n].display= engine;
2053       if (palette) GhSetPalette(n, palette, nColors);
2054 #else
2055       YError("No interactive graphics in this Yorick -- hcp only");
2056 #endif
2057     }
2058   }
2059 
2060   if (keySymbols[3]) {
2061     /* hcp= keyword -- make a new hcp file */
2062     Engine *engine= ghDevices[n].hcp;
2063     char *hcp= 0;
2064     if (YNotNil(keySymbols[3])) hcp= YGetString(keySymbols[3]);
2065 
2066     if (engine) {
2067       ghDevices[n].hcp= 0;
2068       GpKillEngine(engine);
2069       SetHCPname(n, (char *)0);
2070     }
2071 
2072     if (hcp && hcp[0]) {
2073       long len= strlen(hcp);
2074       if (len>3 && strcmp(&hcp[len-3], ".ps")==0) {
2075         engine= GpPSEngine(window_name(n), 0, hcpDump, SetHCPname(n, hcp));
2076         if (!engine) YError("failed to create PostScript file");
2077       } else {
2078         engine= GpCGMEngine(window_name(n), 0, hcpDump, SetHCPname(n, hcp));
2079         if (!engine) YError("failed to create binary CGM file");
2080       }
2081       ghDevices[n].hcp= engine;
2082       if (palette) GhSetPalette(n, palette, nColors);
2083     }
2084   }
2085 
2086   if (keySymbols[4] || keySymbols[3] ||
2087       nGiven || keySymbols[0] || keySymbols[1]) {
2088     if (YNotNil(keySymbols[4]))
2089       /* legends= keyword -- turn on/off legend dumping to hcp file */
2090       ghDevices[n].doLegends= (YGetInteger(keySymbols[4])!=0);
2091     else
2092       ghDevices[n].doLegends= defaultLegends;
2093   }
2094 
2095   if (YNotNil(keySymbols[5])) {
2096     /* dump= keyword -- turn on/off colormap dumping to hcp file */
2097     if (!ghDevices[n].hcp)
2098       YError("dump= keyword not legal without hcp engine -- use hcp_file");
2099     GhDumpColors(n, 1, (YGetInteger(keySymbols[5])!=0));
2100   }
2101 
2102   if (!ghDevices[n].display && !ghDevices[n].hcp) {
2103     /* shut down this graphics window completely */
2104     Drauing *drawing= ghDevices[n].drawing;
2105     ghDevices[n].drawing= 0;
2106     if (drawing) GdKillDrawing(drawing);
2107     GhDeletePalette(n);
2108     if (n==n0) {
2109       /* highest numbered remaining window becomes current window */
2110       for (n=GH_NDEVS-1 ; n>=0 ; n--)
2111         if (ghDevices[n].display || ghDevices[n].hcp) break;
2112       GhSetPlotter(n);
2113     }
2114 
2115   } else {
2116     Drauing *drawing= ghDevices[n].drawing;
2117     if (keySymbols[6]) {
2118       /* style= keyword -- make new drawing */
2119       char *style= YNotNil(keySymbols[6]) ? YGetString(keySymbols[6]) : 0;
2120       if (drawing) {
2121         ghDevices[n].drawing= 0;
2122         GdKillDrawing(drawing);
2123       }
2124       if (!style || !style[0]) style= defaultStyle;
2125       ghDevices[n].drawing= drawing= GdNewDrawing(style? style : "work.gs");
2126 
2127     } else if (!drawing) {
2128       /* supply default drawing */
2129       ghDevices[n].drawing= drawing=
2130         GdNewDrawing(defaultStyle? defaultStyle : "work.gs");
2131     }
2132 
2133     if (!drawing) {
2134       ghDevices[n].drawing= drawing= GdNewDrawing("work.gs");
2135       if (drawing)
2136         YError("failed to create drawing -- bad style sheet name?");
2137       else
2138         YError("failed to create drawing -- Gist work.gs style sheet missing");
2139     }
2140 
2141     /* make this window current */
2142     GhSetPlotter(n);
2143 
2144     /* wait= keyword -- pause until X window is exposed */
2145     wait_for_expose = wait_for_expose &&
2146       YNotNil(keySymbols[7]) && YGetInteger(keySymbols[7]);
2147   }
2148 
2149   Drop(nArgs);
2150   PushLongValue((long)n);
2151 
2152 #ifndef NO_XLIB
2153   if (wait_for_expose) {
2154     Instruction *ipc = yg_pc_resume;
2155     int oops = 0;
2156     if (!ipc) {
2157       oops = gist_expose_wait(ghDevices[n].display, yg_got_expose);
2158       if (!oops) {
2159         yg_blocking = 1;
2160         yg_pc_resume = ym_suspend();
2161       } else if (oops == 2) {
2162         /* window was already exposed */
2163         oops = 0;
2164       }
2165     }
2166     if (ipc || oops) {
2167       yg_got_expose();
2168       YError("window,wait=1 while already waiting for a window");
2169     }
2170   }
2171 #endif
2172 }
2173 
2174 static void yg_alarm(void *);
2175 
2176 void
yg_got_expose(void)2177 yg_got_expose(void)
2178 {
2179   Instruction *ipc = yg_pc_resume;
2180   yg_pc_resume = 0;
2181   if (yg_blocking==2) p_clr_alarm(yg_alarm, 0);
2182   yg_blocking = 0;
2183   if (ipc) ym_resume(ipc);
2184 }
2185 
Y_window_geometry(int argc)2186 void Y_window_geometry(int argc)
2187 {
2188   int win;
2189   double *geom;
2190   Engine *engine;
2191   GpXYMap *map;
2192   long dims[2];
2193 
2194   double one_pixel, dpi, xbias, ybias, width, height;
2195 
2196   if (argc != 1) {
2197     YError("window_geometry takes exactly one, possibly nil, argument");
2198   }
2199   if (YNotNil(sp)) {
2200     win = (int)YGetInteger(sp);
2201   } else {
2202     win = GhGetPlotter();
2203   }
2204   if (win < 0 || win >= GH_NDEVS || ! ghDevices[win].display) {
2205     PushDataBlock(RefNC(&nilDB));
2206     return;
2207   }
2208 
2209   /* NDC -> pixel coordinate transform:
2210    *   XPIX = (int)(XSCALE*XNDC + XOFFSET)
2211    *   YPIX = (int)(YSCALE*YNDC + YOFFSET)
2212    * with:
2213    *   XSCALE = ENGINE->map.x.scale    XOFFSET = ENGINE->map.x.offset - margin
2214    *   YSCALE = ENGINE->map.y.scale    YOFFSET = ENGINE->map.y.offset - margin
2215    * assuming:
2216    *   (XSCALE*XNDC + XOFFSET) >= 0
2217    *   (YSCALE*YNDC + YOFFSET) >= 0
2218    * the reverse transform is:
2219    *   XPIX <= XSCALE*XNDC + XOFFSET < XPIX + 1
2220    *   YPIX <= YSCALE*YNDC + YOFFSET < YPIX + 1
2221    * to avoid rounding errors we choose the middle of the interval:
2222    *   XNDC  =  (XPIX - XOFFSET + 0.5)/XSCALE  =  XBIAS + XPIX*ONE_PIXEL
2223    *   YNDC  =  (YPIX - YOFFSET + 0.5)/YSCALE  =  YBIAS - YPIX*ONE_PIXEL
2224    * with:
2225    *   ONE_PIXEL = 1.0/XSCALE = -1.0/YSCALE
2226    *       XBIAS = (0.5 - XOFFSET)*ONE_PIXEL
2227    *       XBIAS = (YOFFSET - 0.5)*ONE_PIXEL
2228    */
2229 
2230   engine = ghDevices[win].display;
2231   if (engine) {
2232     map= &engine->map;
2233     dpi = ((XEngine *)engine)->dpi;
2234     one_pixel = 2.0/(map->x.scale - map->y.scale);
2235 #define MARGIN(SIDE) (((XEngine *)engine)->SIDE##Margin)
2236     xbias = (MARGIN(left) - map->x.offset + 0.5)/map->x.scale;
2237     ybias = (MARGIN(top)  - map->y.offset + 0.5)/map->y.scale;
2238 #undef MARGIN
2239     width = ((XEngine *)engine)->wtop;
2240     height = ((XEngine *)engine)->htop;
2241   } else {
2242     dpi = one_pixel = xbias = ybias = width = height = 0.0;
2243   }
2244 
2245   /* Build result array: [DPI, ONE_PIXEL, XBIAS, YBIAS, WIDTH, HEIGHT] */
2246   dims[0] = 1L;
2247   dims[1] = 6L;
2248   geom = ypush_d(dims);
2249   geom[0] = dpi;
2250   geom[1] = one_pixel;
2251   geom[2] = xbias;
2252   geom[3] = ybias;
2253   geom[4] = width;
2254   geom[5] = height;
2255 }
2256 
Y_window_exists(int argc)2257 void Y_window_exists(int argc)
2258 {
2259   long n;
2260   if (argc != 1) YError("window_exists takes exactly one argument");
2261   n = YGetInteger(sp);
2262   PushIntValue(((n >= 0 && n < GH_NDEVS && ghDevices[n].display) ? 1 : 0));
2263 }
2264 
Y_window_select(int argc)2265 void Y_window_select(int argc)
2266 {
2267   int n;
2268   if (argc != 1) YError("window_select takes exactly one argument");
2269   n = (int)YGetInteger(sp);
2270   if (n >= 0 && n < GH_NDEVS && ghDevices[n].display) {
2271     GhSetPlotter(n);
2272     PushIntValue(1);
2273   } else {
2274     PushIntValue(0);
2275   }
2276 }
2277 
Y_window_list(int argc)2278 void Y_window_list(int argc)
2279 {
2280   long *p, i, n, dims[2];
2281 
2282   if (argc != 1 || YNotNil(sp)) {
2283     YError("window_list takes exactly one nil argument");
2284   }
2285   for (n=i=0 ; i<GH_NDEVS ; ++i) {
2286     if (ghDevices[i].display) {
2287       ++n;
2288     }
2289   }
2290   if (n >= 1) {
2291     dims[0] = 1;
2292     dims[1] = n;
2293     p = ypush_l(dims);
2294     for (n=i=0 ; i<GH_NDEVS ; ++i) {
2295       if (ghDevices[i].display) {
2296 	p[n++] = i;
2297       }
2298     }
2299   } else {
2300     ypush_nil();
2301   }
2302 }
2303 
2304 #undef N_KEYWORDS
2305 #define N_KEYWORDS 2
2306 static char *hcpKeys[N_KEYWORDS+1]= { "dump", "ps", 0 };
2307 
Y_hcp_file(int nArgs)2308 void Y_hcp_file(int nArgs)
2309 {
2310   Symbol *keySymbols[N_KEYWORDS];
2311   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, hcpKeys, keySymbols);
2312   Engine *engine= hcpDefault;
2313   int gotDump= YNotNil(keySymbols[0]);
2314 
2315   if (gotDump) hcpDump= (YGetInteger(keySymbols[0])!=0);
2316 
2317   if (YNotNil(keySymbols[1])) hcpPSdefault= (YGetInteger(keySymbols[1])!=0);
2318 
2319   if (stack<=sp && YNotNil(stack)) {
2320     char *hcp= YGetString(stack);
2321     long len= Safe_strlen(hcp);
2322 
2323     if (engine) {
2324       hcpDefault= 0;
2325       GpKillEngine(engine);
2326       SetHCPname(-1, (char *)0);
2327       engine= 0;
2328     }
2329 
2330     if (len>3 && strcmp(&hcp[len-3], ".ps")==0) {
2331       engine= GpPSEngine("Yorick default", 0, hcpDump, SetHCPname(-1, hcp));
2332       if (!engine) YError("failed to create PostScript file");
2333     } else if (len>0) {
2334       engine= GpCGMEngine("Yorick default", 0, hcpDump, SetHCPname(-1, hcp));
2335       if (!engine) YError("failed to create binary CGM file");
2336     }
2337 
2338     hcpDefault= engine;
2339     stack++;
2340   } else if (gotDump) {
2341     GhDumpColors(-1, 1, hcpDump);
2342   }
2343   while (stack<=sp) {
2344     if (!stack->ops) stack+= 2;
2345     else YError("hcp_file function takes at most one non-keyword argument");
2346   }
2347 
2348   Drop(nArgs);
2349 }
2350 
Y_hcp_finish(int nArgs)2351 void Y_hcp_finish(int nArgs)
2352 {
2353   /* just return name of current hcp file */
2354   int n= GhGetPlotter();
2355   Array *array;
2356   Engine *engine;
2357 
2358   if (nArgs==1) {
2359     if (YNotNil(sp)) n= YGetInteger(sp);
2360     if (n<-1 || n>=GH_NDEVS)
2361       YError("hcp_finish argument must be -1 or a graphics window number");
2362   } else if (nArgs) {
2363     YError("hcp_finish takes zero or one arguments");
2364   }
2365 
2366   array= PushDataBlock(NewArray(&stringStruct, (Dimension *)0));
2367   array->value.q[0]= p_strcpy(GetHCPname(n));
2368 
2369   if (n>=0) engine= ghDevices[n].hcp? ghDevices[n].hcp : hcpDefault;
2370   else engine= hcpDefault;
2371   if (engine) {
2372     if (engine==hcpDefault) {
2373       hcpDefault= 0;
2374     } else {
2375       ghDevices[n].hcp= 0;
2376     }
2377     GpKillEngine(engine);
2378     SetHCPname(n, (char *)0);
2379   }
2380 }
2381 
Y_plsys(int nArgs)2382 void Y_plsys(int nArgs)
2383 {
2384   int n0;
2385   if (nArgs!=1) YError("plsys takes exactly one argument");
2386 
2387   CheckDefaultWindow();
2388   n0= GdGetSystem();
2389 
2390   if (YNotNil(sp)) {
2391     int n= (int)YGetInteger(sp);
2392     if (GdSetSystem(n)!=E_SYSTEM && n!=0)
2393       YError("no such coordinate system exists in current graphics window");
2394   }
2395 
2396   PushLongValue((long)n0);
2397 }
2398 
2399 #undef N_KEYWORDS
2400 #define N_KEYWORDS 2
2401 static char *paletteKeys[N_KEYWORDS+1]= { "ntsc", "query", 0 };
2402 
Y_palette(int nArgs)2403 void Y_palette(int nArgs)
2404 {
2405   Symbol *keySymbols[N_KEYWORDS];
2406   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, paletteKeys, keySymbols);
2407   Operand op;
2408   GpColorCell *palette= 0;
2409   unsigned char *red= 0, *green= 0, *blue= 0, *gray= 0;
2410   int i, iPass, nColors, nDevice, query= 0;
2411   Engine *engine;
2412   int sourceDevice= -2;
2413   char **name= 0;
2414 
2415   CheckDefaultWindow();
2416   nDevice= GhGetPlotter();
2417   engine= ghDevices[nDevice].display;
2418   if (!engine) engine= ghDevices[nDevice].hcp;
2419 
2420   if (YNotNil(keySymbols[1])) {
2421     Dimension *dims= tmpDims;
2422     tmpDims= 0;
2423     FreeDimension(dims);
2424     query= (YGetInteger(keySymbols[1])!=0);
2425   }
2426 
2427   iPass= nColors= 0;
2428   while (stack<=sp) {
2429     if (!stack->ops) { stack+= 2; continue; }
2430     if (iPass>3)
2431       YError("palette takes at most red, green, blue, gray arguments");
2432 
2433     if (query) {
2434       if (stack->ops!=&referenceSym)
2435         YError("palette query needs simple variable references as arguments");
2436       if (iPass==0) nColors= GpGetPalette(engine, &palette);
2437       if (nColors>0) {
2438         Array *array;
2439         tmpDims= NewDimension((long)nColors, 1L, (Dimension *)0);
2440         array= PushDataBlock(NewArray(&charStruct, tmpDims));
2441         red= (unsigned char *)array->value.c;
2442         if (iPass==0)
2443           for (i=0 ; i<nColors ; i++) red[i]=(unsigned char)(P_R(palette[i]));
2444         else if (iPass==1)
2445           for (i=0 ; i<nColors ; i++) red[i]=(unsigned char)(P_G(palette[i]));
2446         else if (iPass==2)
2447           for (i=0 ; i<nColors ; i++) red[i]=(unsigned char)(P_B(palette[i]));
2448         else if (iPass==3)
2449           for (i=0 ; i<nColors ; i++)
2450             red[i]=(unsigned char)((P_R(palette[i])+P_G(palette[i])+
2451                                     P_B(palette[i]))/3);
2452       } else {
2453         PushDataBlock(RefNC(&nilDB));
2454       }
2455       PopTo(&globTab[stack->index]);
2456 
2457     } else {
2458       stack->ops->FormOperand(stack, &op);
2459       if (iPass==0) {
2460         if (op.ops==&stringOps) {
2461           /* palette, filename */
2462           name= op.value;
2463 
2464         } else if (!op.type.dims) {
2465           /* palette, source_window */
2466           if (op.ops->promoteID>T_LONG)
2467             YError("palette source window number must be an integer");
2468           op.ops->ToLong(&op);
2469           sourceDevice= (int)(*((long *)op.value));
2470           if (sourceDevice<0 || sourceDevice>=GH_NDEVS ||
2471               (!(engine= ghDevices[sourceDevice].display) &&
2472                !(engine= ghDevices[sourceDevice].hcp)))
2473             YError("specified palette source window does not exist");
2474           nColors= GpGetPalette(engine, &palette);
2475 
2476         } else {
2477           /* palette, red, green, blue */
2478           op.ops->ToChar(&op);
2479           red= op.value;
2480           nColors= (int)op.type.number;
2481         }
2482 
2483         if (nColors>256)
2484           YError("Gist palettes can never have more than 256 colors");
2485 
2486       } else {
2487         /* palette, red, green, blue */
2488         if (!red) YError("garbled arguments to palette command");
2489         op.ops->ToChar(&op);
2490         if (op.type.number != nColors)
2491           YError("red, green, blue, and gray arguments must be same length");
2492         if (iPass==1) green= op.value;
2493         if (iPass==2) blue= op.value;
2494         if (iPass==3) gray= op.value;
2495       }
2496     }
2497 
2498     iPass++;
2499     stack++;
2500   }
2501 
2502   if (!query) {
2503     if (sourceDevice!=nDevice) {
2504       /* be sure to preserve dump=1 setting even if hcp palette
2505          is deleted */
2506       int dump;
2507       if (hcpDefault) dump= GhGetColorMode(hcpDefault);
2508       else dump= 0;
2509       GhDeletePalette(nDevice);
2510       if (hcpDefault) GhDumpColors(-1, 1, dump);
2511     }
2512     if (red || palette) {
2513       if (red) {
2514         if (iPass<3)
2515           YError("palette needs at least red, green, and blue components");
2516         /* palette is unprotected against asynchronous interrupts...
2517            fix this someday */
2518         palette = p_malloc(sizeof(GpColorCell)*nColors);
2519         /* palette, red, green, blue malloc'ed like GhReadPalette */
2520         for (i=0 ; i<nColors ; i++) {
2521           palette[i] = P_RGB(red[i], green[i], blue[i]);
2522           /* if (gray) palette[i].gray = gray[i]; */
2523         }
2524       }
2525       if (!gray) {
2526         if (YNotNil(keySymbols[0]) && YGetInteger(keySymbols[0])!=0)
2527           GpPutNTSC(nColors, palette);
2528         else
2529           GpPutGray(nColors, palette);
2530       }
2531       GhSetPalette(nDevice, palette, nColors);
2532 
2533     } else if (name) {
2534       nColors= GhReadPalette(nDevice, name[0], &palette, maxColors);
2535       if (nColors<=0)
2536         YError("no such palette -- missing Gist palette file?");
2537 
2538     } else {
2539       YError("palette needs at least one non-keyword argument");
2540     }
2541   }
2542 }
2543 
2544 /*--------------------------------------------------------------------------*/
2545 
Y_fma(int nArgs)2546 void Y_fma(int nArgs)
2547 {
2548   if (nArgs) YError("fma takes exactly zero argument");
2549   CheckDefaultWindow();
2550   if (hcpOnFMA) CheckPalette();
2551   curElement= -1;
2552   GhFMA();
2553 }
2554 
Y_redraw(int nArgs)2555 void Y_redraw(int nArgs)
2556 {
2557   if (nArgs) YError("redraw takes exactly zero argument");
2558   CheckDefaultWindow();
2559   GhRedraw();
2560 }
2561 
Y_hcp(int nArgs)2562 void Y_hcp(int nArgs)
2563 {
2564   if (nArgs) YError("hcp takes exactly zero argument");
2565   CheckDefaultWindow();
2566   CheckPalette();
2567   GhHCP();
2568 }
2569 
Y_hcpon(int nArgs)2570 void Y_hcpon(int nArgs)
2571 {
2572   if (nArgs) YError("hcpon takes exactly zero argument");
2573   CheckDefaultWindow();
2574   hcpOnFMA= 1;
2575   GhFMAMode(1, 2);
2576 }
2577 
Y_hcpoff(int nArgs)2578 void Y_hcpoff(int nArgs)
2579 {
2580   if (nArgs) YError("hcpoff takes exactly zero argument");
2581   CheckDefaultWindow();
2582   hcpOnFMA= 0;
2583   GhFMAMode(0, 2);
2584 }
2585 
Y_animate(int nArgs)2586 void Y_animate(int nArgs)
2587 {
2588   int i= 3;  /* default is to toggle */
2589 
2590   if (nArgs==1 && YNotNil(sp)) i= (int)YGetInteger(sp);
2591   else if (nArgs>1) YError("animate takes zero or one argument");
2592 
2593   CheckDefaultWindow();
2594 
2595   curElement= -1;
2596   GhFMAMode(2, i);
2597 }
2598 
2599 /*--------------------------------------------------------------------------*/
2600 
2601 static long prop3sizes[10]= {0, 8, 2, 5, 5, 4, 3, 7, 1, 3};
2602 static long prop4sizes[10]= {0, 8, 1, 3, 1, 1, 3, 4, 4, 1};
2603 static long prop5sizes[10]= {0, 3, 5, 2, 5, 6, 7, 9, 3, 5};
2604 
2605 static int curIX= -1, curIXc= -1;
2606 static char specialMarkers[5]= ".+*ox";
2607 
Y_plq(int nArgs)2608 void Y_plq(int nArgs)
2609 {
2610   int type, n_element= 0, n_contour= 0;
2611 
2612   if (nArgs==1) {
2613     if (YNotNil(sp)) n_element= (int)YGetInteger(sp);
2614   } else if (nArgs==2) {
2615     if (YNotNil(sp-1)) n_element= (int)YGetInteger(sp-1);
2616     if (YNotNil(sp)) n_contour= (int)YGetInteger(sp);
2617   } else if (nArgs>2) {
2618     YError("plq function takes no more than two arguments");
2619   }
2620   Drop(nArgs);
2621 
2622   /* Yorick uses 1-origin element numbering, Gist uses 0-origin */
2623   n_element--;
2624   n_contour--;
2625 
2626   if (n_element>=0) {
2627     /* retrieve specified element */
2628     type= GdSetElement(n_element);
2629     if (n_contour>=0) {
2630       if (type!=E_CONTOURS)
2631         YError("current graphical element is not contours in pledit");
2632       type= GdSetContour(n_contour);
2633     }
2634     curElement= -6666; /* differs from -1 to allow pledit after plq */
2635     curIX= n_element;  /* need these, too */
2636     curIXc= n_contour;
2637     if (type==E_LINES) type= 1;
2638     else if (type==E_DISJOINT) type= 2;
2639     else if (type==E_TEXT) type= 3;
2640     else if (type==E_MESH) type= 4;
2641     else if (type==E_FILLED) type= 5;
2642     else if (type==E_VECTORS) type= 6;
2643     else if (type==E_CONTOURS) type= 7;
2644     else if (type==E_CELLS) type= 8;
2645     else if (type==E_POLYS) type= 9;
2646     else type= 0;
2647 
2648     if (CalledAsSubroutine()) {
2649       /* return printed summary of keyword values */
2650       char line[120];
2651       PrintInit(YputsOut);
2652 
2653       if (type==0) {
2654         sprintf(line, "<no such object>  element# %d", n_element+1);
2655         PrintFunc(line);
2656         if (n_contour>=0) {
2657           sprintf(line, "  contour# %d", n_contour+1);
2658           PrintFunc(line);
2659         }
2660         ForceNewline();
2661 
2662       } else if (type==1) {
2663         sprintf(line, "plg  element# %d", n_element+1);
2664         PrintFunc(line);
2665         if (n_contour>=0) {
2666           sprintf(line, "  contour# %d", n_contour+1);
2667           PrintFunc(line);
2668           ForceNewline();
2669           sprintf(line, "  at level value %g", gistD.levels[n_contour]);
2670           PrintFunc(line);
2671         }
2672         ForceNewline();
2673         PrintHideLegend(line, type);
2674         PrintColor(line, gistA.l.color, 1);
2675         PrintTypeWidth(line, 3);
2676         PrintMarks(line, 3);
2677         sprintf(line, "rays= %d,", gistA.dl.rays);
2678         PrintFunc(line);
2679         ForceNewline();
2680         sprintf(line,
2681                 "  arrowl= %.2f, arroww= %.2f, rspace= %.5f, rphase= %.5f,",
2682                 Safe_dbl(gistA.dl.arrowL), Safe_dbl(gistA.dl.arrowW),
2683                 Safe_dbl(gistA.dl.rSpace), Safe_dbl(gistA.dl.rPhase));
2684         PrintFunc(line);
2685         ForceNewline();
2686         sprintf(line, "smooth= %d,  closed= %d",
2687                 gistA.dl.smooth, gistA.dl.closed);
2688         PrintFunc(line);
2689         ForceNewline();
2690 
2691       } else if (type==2) {
2692         sprintf(line, "pldj  element# %d", n_element+1);
2693         PrintFunc(line);
2694         ForceNewline();
2695         PrintHideLegend(line, type);
2696         PrintColor(line, gistA.l.color, 1);
2697         PrintTypeWidth(line, 2);
2698 
2699       } else if (type==3) {
2700         sprintf(line, "plt  element# %d", n_element+1);
2701         PrintFunc(line);
2702         ForceNewline();
2703         PrintHideLegend(line, type);
2704         PrintColor(line, gistA.t.color, 3);
2705         sprintf(line, "text= %.80s", gistD.text);
2706         PrintFunc(line);
2707         ForceNewline();
2708 
2709       } else if (type==4) {
2710         sprintf(line, "plm  element# %d", n_element+1);
2711         PrintFunc(line);
2712         ForceNewline();
2713         PrintHideLegend(line, type);
2714         PrintColor(line, gistA.l.color, 1);
2715         PrintTypeWidth(line, 2);
2716         PrintRegion(line, 1);
2717         sprintf(line, "boundary= %d, inhibit= %d", gistD.boundary,
2718                 gistD.inhibit);
2719         PrintFunc(line);
2720         ForceNewline();
2721 
2722       } else if (type==5) {
2723         sprintf(line, "plf  element# %d", n_element+1);
2724         PrintFunc(line);
2725         ForceNewline();
2726         PrintHideLegend(line, type);
2727         sprintf(line, "edges= %d, e", gistA.e.type!=L_NONE);
2728         PrintFunc(line);
2729         PrintColor(line, gistA.e.color, 1);
2730         sprintf(line, "ewidth= %.2f", Safe_dbl(gistA.e.width));
2731         PrintFunc(line);
2732         ForceNewline();
2733         PrintRegion(line, 2);
2734 
2735       } else if (type==6) {
2736         sprintf(line, "plv  element# %d", n_element+1);
2737         PrintFunc(line);
2738         ForceNewline();
2739         PrintHideLegend(line, type);
2740         PrintColor(line, gistA.l.color, 1);
2741         sprintf(line, "width= %.2f,", Safe_dbl(gistA.l.width));
2742         PrintFunc(line);
2743         ForceNewline();
2744         sprintf(line, "hollow= %d,  aspect= %.4f,", gistA.vect.hollow,
2745                 Safe_dbl(gistA.vect.aspect));
2746         PrintFunc(line);
2747         ForceNewline();
2748         PrintRegion(line, 3);
2749         sprintf(line, "scale= %g", gistD.scale);
2750         PrintFunc(line);
2751         ForceNewline();
2752 
2753       } else if (type==7) {
2754         int i;
2755         sprintf(line, "plc  element# %d", n_element+1);
2756         PrintFunc(line);
2757         ForceNewline();
2758         PrintHideLegend(line, type);
2759         PrintColor(line, gistA.l.color, 1);
2760         PrintTypeWidth(line, 3);
2761         PrintMarks(line, 3);
2762         sprintf(line, "smooth= %d,", gistA.dl.smooth);
2763         PrintFunc(line);
2764         ForceNewline();
2765         PrintRegion(line, 2);
2766         sprintf(line, "%d contour levels, levs=", gistD.nLevels);
2767         PrintFunc(line);
2768         ForceNewline();
2769         PrintFunc("[");
2770         if (gistD.nLevels>0) {
2771           for (i=0 ; ; i++) {
2772             sprintf(line, "%g", gistD.levels[i]);
2773             PrintFunc(line);
2774             if (i==gistD.nLevels-1) break;
2775             PrintFunc(",");
2776             PermitNewline(0);
2777           }
2778         }
2779         PrintFunc("]");
2780         ForceNewline();
2781 
2782       } else if (type==8) {
2783         sprintf(line, "pli  element# %d", n_element+1);
2784         PrintFunc(line);
2785         ForceNewline();
2786         PrintHideLegend(line, type);
2787         sprintf(line, "x0= %g,  y0= %g,  x1= %g,  y1= %g",
2788                 gistD.px, gistD.py, gistD.qx, gistD.qy);
2789         PrintFunc(line);
2790         ForceNewline();
2791 
2792       } else if (type==9) {
2793         sprintf(line, "plfp  element# %d", n_element+1);
2794         PrintFunc(line);
2795         ForceNewline();
2796         PrintHideLegend(line, type);
2797         sprintf(line, "%d polygons", gistD.n);
2798         PrintFunc(line);
2799         ForceNewline();
2800       }
2801 
2802     } else {
2803       /* return properties array */
2804       Dimension *dims= NewDimension(6L, 1L, (Dimension *)0);
2805       Array *array= PushDataBlock(NewArray(&pointerStruct, dims));
2806       void **p= array->value.p;
2807       char **legend;
2808       int *ival;
2809       double *dval;
2810       long *lval;
2811       dims->references--;
2812 
2813       dims= NewDimension(2L, 1L, (Dimension *)0);
2814       p[0]= (NewArray(&intStruct, dims))->value.c;
2815       dims->references--;
2816       p[1]= (NewArray(&stringStruct, (Dimension *)0))->value.c;
2817       p[2]= MakePropArray(&intStruct, prop3sizes[type]);
2818       p[3]= MakePropArray(&doubleStruct, prop4sizes[type]);
2819       p[4]= MakePropArray(&longStruct, prop5sizes[type]);
2820 
2821       ival= (int *)p[0];
2822       ival[0]= type;
2823       ival[1]= type? gistD.hidden : 0;
2824 
2825       if (type) {
2826         legend= (char **)p[1];
2827         legend[0]= p_strcpy(gistD.legend);
2828         if ((type==1 || type==7) && legend[0] && legend[0][0]=='\001') {
2829           if (gistA.m.type>=' ' && gistA.m.type<'\177')
2830             legend[0][0]= (char)gistA.m.type;
2831           else if (gistA.m.type>=1 && gistA.m.type<=5)
2832             legend[0][0]= specialMarkers[gistA.m.type-1];
2833           else
2834             legend[0][0]= '?';
2835         }
2836       }
2837 
2838       ival= (int *)p[2];
2839       dval= (double *)p[3];
2840       lval= (long *)p[4];
2841       if (type==1) {                 /* plg */
2842         ival[0]= gistA.l.color;
2843         ival[1]= gistA.l.type;
2844         ival[2]= gistA.dl.marks;
2845         ival[3]= gistA.m.color;
2846         ival[4]= gistA.m.type;
2847         ival[5]= gistA.dl.rays;
2848         ival[6]= gistA.dl.closed;
2849         ival[7]= gistA.dl.smooth;
2850         dval[0]= gistA.l.width;
2851         dval[1]= gistA.m.size;
2852         dval[2]= gistA.dl.mSpace;
2853         dval[3]= gistA.dl.mPhase;
2854         dval[4]= gistA.dl.rSpace;
2855         dval[5]= gistA.dl.rPhase;
2856         dval[6]= gistA.dl.arrowL;
2857         dval[7]= gistA.dl.arrowW;
2858         lval[0]= gistD.n;
2859         lval[1]= ((char *)gistD.x)-((char *)0);
2860         lval[2]= ((char *)gistD.y)-((char *)0);
2861       } else if (type==2) {           /* pldj */
2862         ival[0]= gistA.l.color;
2863         ival[1]= gistA.l.type;
2864         dval[0]= gistA.l.width;
2865         lval[0]= gistD.n;
2866         lval[1]= ((char *)gistD.x)-((char *)0);
2867         lval[2]= ((char *)gistD.y)-((char *)0);
2868         lval[3]= ((char *)gistD.xq)-((char *)0);
2869         lval[4]= ((char *)gistD.yq)-((char *)0);
2870       } else if (type==3) {           /* plt */
2871         ival[0]= gistA.t.color;
2872         ival[1]= gistA.t.font;
2873         ival[2]= gistA.t.orient;
2874         ival[3]= (gistA.t.alignH | (gistA.t.alignV<<2));
2875         ival[4]= gistA.t.opaque;
2876         dval[0]= gistA.t.height/ONE_POINT;
2877         dval[1]= gistD.x0;
2878         dval[2]= gistD.y0;
2879         lval[0]= Safe_strlen(gistD.text);
2880         lval[1]= ((char *)gistD.text)-((char *)0);
2881       } else if (type==4) {           /* plm */
2882         ival[0]= gistA.l.color;
2883         ival[1]= gistA.l.type;
2884         ival[2]= gistD.region;
2885         ival[3]= gistD.boundary;
2886         ival[4]= gistD.inhibit;
2887         dval[0]= gistA.l.width;
2888         lval[0]= gistD.mesh.iMax;
2889         lval[1]= gistD.mesh.jMax;
2890         lval[2]= ((char *)gistD.mesh.x)-((char *)0);
2891         lval[3]= ((char *)gistD.mesh.y)-((char *)0);
2892         lval[4]= ((char *)gistD.mesh.reg)-((char *)0);
2893       } else if (type==5) {           /* plf */
2894         ival[0]= gistD.region;
2895         ival[1]= gistA.e.type!=L_NONE;
2896         ival[2]= gistA.e.color;
2897 	ival[3]= gistA.rgb;
2898         dval[0]= gistA.e.width;
2899         lval[0]= gistD.mesh.iMax;
2900         lval[1]= gistD.mesh.jMax;
2901         lval[2]= ((char *)gistD.mesh.x)-((char *)0);
2902         lval[3]= ((char *)gistD.mesh.y)-((char *)0);
2903         lval[4]= ((char *)gistD.mesh.reg)-((char *)0);
2904         lval[5]= ((char *)gistD.colors)-((char *)0);
2905       } else if (type==6) {           /* plv */
2906         ival[0]= gistD.region;
2907         ival[1]= gistA.l.color;
2908         ival[2]= gistA.vect.hollow;
2909         dval[0]= gistA.l.width;
2910         dval[1]= gistA.vect.aspect;
2911         dval[2]= gistD.scale;
2912         lval[0]= gistD.mesh.iMax;
2913         lval[1]= gistD.mesh.jMax;
2914         lval[2]= ((char *)gistD.mesh.x)-((char *)0);
2915         lval[3]= ((char *)gistD.mesh.y)-((char *)0);
2916         lval[4]= ((char *)gistD.mesh.reg)-((char *)0);
2917         lval[5]= ((char *)gistD.u)-((char *)0);
2918         lval[6]= ((char *)gistD.v)-((char *)0);
2919       } else if (type==7) {           /* plc */
2920         ival[0]= gistD.region;
2921         ival[1]= gistA.l.color;
2922         ival[2]= gistA.l.type;
2923         ival[3]= gistA.dl.marks;
2924         ival[4]= gistA.m.color;
2925         ival[5]= gistA.m.type;
2926         ival[6]= gistA.dl.smooth;
2927         dval[0]= gistA.l.width;
2928         dval[1]= gistA.m.size;
2929         dval[2]= gistA.dl.mSpace;
2930         dval[3]= gistA.dl.mPhase;
2931         lval[0]= gistD.mesh.iMax;
2932         lval[1]= gistD.mesh.jMax;
2933         lval[2]= ((char *)gistD.mesh.x)-((char *)0);
2934         lval[3]= ((char *)gistD.mesh.y)-((char *)0);
2935         lval[4]= ((char *)gistD.mesh.reg)-((char *)0);
2936         lval[5]= ((char *)gistD.z)-((char *)0);
2937         lval[6]= ((char *)gistD.mesh.triangle)-((char *)0);
2938         lval[7]= gistD.nLevels;
2939         lval[8]= ((char *)gistD.levels)-((char *)0);
2940       } else if (type==8) {           /* pli */
2941 	ival[0]= gistA.rgb;
2942         dval[0]= gistD.px;
2943         dval[1]= gistD.py;
2944         dval[2]= gistD.qx;
2945         dval[3]= gistD.qy;
2946         lval[0]= gistD.width;
2947         lval[1]= gistD.height;
2948         lval[2]= ((char *)gistD.colors)-((char *)0);
2949       } else if (type==9) {           /* plfp */
2950 	ival[0]= gistA.e.type;
2951 	ival[1]= gistA.e.color;
2952 	ival[2]= gistA.rgb;
2953 	dval[0]= gistA.e.width;
2954         lval[0]= gistD.n;
2955         lval[1]= ((char *)gistD.x)-((char *)0);
2956         lval[2]= ((char *)gistD.y)-((char *)0);
2957         lval[3]= ((char *)gistD.colors)-((char *)0);
2958         lval[4]= ((char *)gistD.pn)-((char *)0);
2959       }
2960     }
2961 
2962   } else if (n_contour>=0) {
2963     YError("contour number cannot be specified without element number");
2964 
2965   } else {
2966     char line[16];
2967     int i, offset;
2968     /* print list of legends... */
2969     if (CalledAsSubroutine()) {
2970       /* ...to terminal */
2971       PrintInit(YputsOut);
2972     } else {
2973       /* ...to result string array */
2974       PrintInit(&PutsAsArray);
2975       PushDataBlock(NewArray(&stringStruct, (Dimension *)0));
2976     }
2977 
2978     curElement= -1;
2979     for (i=0 ; (type= GdSetElement(i))!=E_NONE ; i++) {
2980       sprintf(line, "%s%2d: ", gistD.hidden?"(H)":"", i+1);
2981       PrintFunc(line);
2982       offset= 0;
2983       if ((type==E_LINES || type==E_CONTOURS) && gistD.legend &&
2984           gistD.legend[0]=='\001') {
2985         char marker[2];
2986         marker[1]= '\0';
2987         if (gistA.m.type>=' ' && gistA.m.type<'\177')
2988           marker[0]= (char)gistA.m.type;
2989         else if (gistA.m.type>=1 && gistA.m.type<=5)
2990           marker[0]= specialMarkers[gistA.m.type-1];
2991         else
2992           marker[0]= '?';
2993         PrintFunc(marker);
2994         offset= 1;
2995       }
2996       if (gistD.legend) PrintFunc(gistD.legend+offset);
2997       ForceNewline();
2998     }
2999   }
3000 }
3001 
PrintHideLegend(char * line,int type)3002 static void PrintHideLegend(char *line, int type)
3003 {
3004   int offset= 0;
3005   char marker[5];
3006   marker[0]= '\0';
3007   sprintf(line, "hide= %d,", gistD.hidden);
3008   PrintFunc(line);
3009   ForceNewline();
3010   if ((type==1 || type==7) && gistD.legend && gistD.legend[0]=='\001') {
3011     marker[0]= '\\';
3012     marker[1]= marker[2]= '0';
3013     marker[3]= '1';
3014     marker[4]= '\0';
3015     offset= 1;
3016   }
3017   sprintf(line, "legend= \"%s%.104s\",", marker,
3018           gistD.legend? gistD.legend+offset : "");
3019   PrintFunc(line);
3020   ForceNewline();
3021 }
3022 
PrintColor(char * line,int color,int suffix)3023 static void PrintColor(char *line, int color, int suffix)
3024 {
3025   if (color>=0) {
3026     sprintf(line, "color= %d,", color);
3027     PrintFunc(line);
3028   } else if (color==P_FG) PrintFunc("color= \"fg\"");
3029   else if (color==P_BG) PrintFunc("color= \"bg\"");
3030   else if (color==P_RED) PrintFunc("color= \"red\"");
3031   else if (color==P_GREEN) PrintFunc("color= \"green\"");
3032   else if (color==P_BLUE) PrintFunc("color= \"blue\"");
3033   else if (color==P_CYAN) PrintFunc("color= \"cyan\"");
3034   else if (color==P_MAGENTA) PrintFunc("color= \"magenta\"");
3035   else if (color==P_YELLOW) PrintFunc("color= \"yellow\"");
3036   else if (color==P_GRAYD) PrintFunc("color= \"grayd\"");
3037   else if (color==P_GRAYC) PrintFunc("color= \"grayc\"");
3038   else if (color==P_GRAYB) PrintFunc("color= \"grayb\"");
3039   else if (color==P_GRAYA) PrintFunc("color= \"graya\"");
3040   else PrintFunc("color= <unknown>");
3041   PrintSuffix(suffix);
3042 }
3043 
PrintTypeWidth(char * line,int suffix)3044 static void PrintTypeWidth(char *line, int suffix)
3045 {
3046   if (gistA.l.type==L_NONE) PrintFunc("type= \"none\"");
3047   else if (gistA.l.type==L_SOLID) PrintFunc("type= \"solid\"");
3048   else if (gistA.l.type==L_DASH) PrintFunc("type= \"dash\"");
3049   else if (gistA.l.type==L_DOT) PrintFunc("type= \"dot\"");
3050   else if (gistA.l.type==L_DASHDOT) PrintFunc("type= \"dashdot\"");
3051   else if (gistA.l.type==L_DASHDOTDOT) PrintFunc("type= \"dashdotdot\"");
3052   else PrintFunc("type= <unknown>");
3053   sprintf(line, ",  width= %.2f", Safe_dbl(gistA.l.width));
3054   PrintFunc(line);
3055   PrintSuffix(suffix);
3056 }
3057 
PrintMarks(char * line,int suffix)3058 static void PrintMarks(char *line, int suffix)
3059 {
3060   sprintf(line, "marks= %d,  mcolor= 0x%02lx,  ",
3061           gistA.dl.marks, gistA.m.color);
3062   PrintFunc(line);
3063   if (gistA.m.type<=' ' || gistA.m.type>=0xff)
3064     sprintf(line, "marker= '\\%o',", gistA.m.type);
3065   else
3066     sprintf(line, "marker= '%c',", gistA.m.type);
3067   PrintFunc(line);
3068   ForceNewline();
3069   sprintf(line,
3070           "  msize= %.2f, mspace= %.5f, mphase= %.5f",
3071           Safe_dbl(gistA.m.size),
3072           Safe_dbl(gistA.dl.mSpace), Safe_dbl(gistA.dl.mPhase));
3073   PrintFunc(line);
3074   PrintSuffix(suffix);
3075 }
3076 
PrintRegion(char * line,int suffix)3077 static void PrintRegion(char *line, int suffix)
3078 {
3079   sprintf(line, "region= %d", gistD.region);
3080   PrintFunc(line);
3081   PrintSuffix(suffix);
3082 }
3083 
PrintSuffix(int suffix)3084 static void PrintSuffix(int suffix)
3085 {
3086   if (suffix==1) PrintFunc(",  ");
3087   else if (suffix==3) PrintFunc(",");
3088   if (suffix&2) ForceNewline();
3089 }
3090 
Safe_dbl(double x)3091 static double Safe_dbl(double x)
3092 {
3093   if (x>1000.0) return 1000.0;
3094   else if (x<-1000.0) return -1000.0;
3095   else return x;
3096 }
3097 
MakePropArray(StructDef * base,long size)3098 static void *MakePropArray(StructDef *base, long size)
3099 {
3100   Array *array;
3101   if (!size) return 0;
3102   array= NewArray(base, NewDimension(size, 1L, (Dimension *)0));
3103   array->type.dims->references--;
3104   return array->value.c;
3105 }
3106 
3107 /*--------------------------------------------------------------------------*/
3108 
3109 #undef N_KEYWORDS
3110 #define N_KEYWORDS 36
3111 static char *editKeys[N_KEYWORDS+1]= {
3112   "legend", "hide",
3113   "color", "type", "width",
3114   "marks", "mcolor", "marker", "msize", "mspace", "mphase",
3115   "rays", "arrowl", "arroww", "rspace", "rphase", "closed", "smooth",
3116   "font", "height", "orient", "justify", "opaque",
3117   "hollow", "aspect", "region", "boundary", "levs", "scale", "scalem",
3118   "dx", "dy", "edges", "ecolor", "ewidth", "inhibit", 0 };
3119 
Y_pledit(int nArgs)3120 void Y_pledit(int nArgs)
3121 {
3122   int type= 0, n_element= 0, n_contour= 0;
3123   int changes= 0, resetLevs= 0;
3124   Symbol *keySymbols[N_KEYWORDS];
3125   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, editKeys, keySymbols);
3126   int iPass= 0;
3127   char *legend= 0;
3128 
3129   while (stack<=sp) {
3130     if (!stack->ops) { stack+= 2; continue; }
3131 
3132     if (iPass==0) {
3133       if (YNotNil(stack)) n_element= (int)YGetInteger(stack);
3134     } else if (iPass==1) {
3135       if (YNotNil(stack)) n_contour= (int)YGetInteger(stack);
3136     } else {
3137       YError("pledit takes at most two non-keyword arguments");
3138     }
3139 
3140     iPass++;
3141     stack++;
3142   }
3143 
3144   /* Yorick uses 1-origin element numbering, Gist uses 0-origin */
3145   n_element--;
3146   n_contour--;
3147   if (n_element<0) {
3148     if (curElement>=0) {
3149       n_element= GdFindIndex(curElement);
3150       if (n_element<0) {
3151         curElement= -1;
3152         YError("lost current graphical element for pledit (BUG?)");
3153       }
3154     } else if (curElement==-6666) {
3155       n_element= curIX;
3156       n_contour= curIXc;
3157     } else {
3158       YError("no current graphical element for pledit");
3159     }
3160   }
3161   if (n_element>=0 || n_contour>=0) {
3162     /* retrieve specified element */
3163     if (n_element>=0) type= GdSetElement(n_element);
3164     if (n_contour>=0) {
3165       if (type!=E_CONTOURS)
3166         YError("current graphical element is not contours in pledit");
3167       type= GdSetContour(n_contour);
3168     }
3169     curElement= -6666;  /* differs from -1 to allow pledit after plq */
3170     curIX= n_element;   /* need these, too */
3171     curIXc= n_contour;
3172     if (type==E_LINES) type= 1;
3173     else if (type==E_DISJOINT) type= 2;
3174     else if (type==E_TEXT) type= 3;
3175     else if (type==E_MESH) type= 4;
3176     else if (type==E_FILLED) type= 5;
3177     else if (type==E_VECTORS) type= 6;
3178     else if (type==E_CONTOURS) type= 7;
3179     else if (type==E_CELLS) type= 8;
3180     else type= 0;
3181     if (type==0) YError("no such graphical element for pledit");
3182   }
3183 
3184   /* legend and hide */
3185   if (keySymbols[0] && YNotNil(keySymbols[0]))
3186     legend= YGetString(keySymbols[0]);
3187   if (YNotNil(keySymbols[1])) gistD.hidden= (YGetInteger(keySymbols[1])!=0);
3188 
3189   /* GdLines properties */
3190   if (YNotNil(keySymbols[2]))
3191     gistA.l.color= gistA.m.color= gistA.f.color=
3192       gistA.t.color= YgetColor(keySymbols[2]);
3193   if (YNotNil(keySymbols[3]))
3194     gistA.l.type= GetLineType(keySymbols[3]);
3195   if (YNotNil(keySymbols[4]))
3196     gistA.l.width= YGetReal(keySymbols[4]);
3197   if (YNotNil(keySymbols[5]))
3198     gistA.dl.marks= (YGetInteger(keySymbols[5])!=0);
3199   if (YNotNil(keySymbols[6]))
3200     gistA.m.color= YgetColor(keySymbols[6]);
3201   if (YNotNil(keySymbols[7]))
3202     gistA.m.type= (int)YGetInteger(keySymbols[7]);
3203   if (YNotNil(keySymbols[8]))
3204     gistA.m.size= YGetReal(keySymbols[8]);
3205   if (YNotNil(keySymbols[9]))
3206     gistA.dl.mSpace= YGetReal(keySymbols[9]);
3207   if (YNotNil(keySymbols[10]))
3208     gistA.dl.mPhase= YGetReal(keySymbols[10]);
3209   if (YNotNil(keySymbols[11]))
3210     gistA.dl.rays= (YGetInteger(keySymbols[11])!=0);
3211   if (YNotNil(keySymbols[12]))
3212     gistA.dl.arrowL= YGetReal(keySymbols[12]);
3213   if (YNotNil(keySymbols[13]))
3214     gistA.dl.arrowW= YGetReal(keySymbols[13]);
3215   if (YNotNil(keySymbols[14]))
3216     gistA.dl.rSpace= YGetReal(keySymbols[14]);
3217   if (YNotNil(keySymbols[15]))
3218     gistA.dl.rPhase= YGetReal(keySymbols[15]);
3219   if (YNotNil(keySymbols[16]))
3220     gistA.dl.closed= (YGetInteger(keySymbols[16])!=0);
3221   if (YNotNil(keySymbols[17]))
3222     gistA.dl.smooth= (YGetInteger(keySymbols[17])!=0);
3223 
3224   /* GdText properties */
3225   if (YNotNil(keySymbols[18]))
3226     gistA.t.font= GetFont(keySymbols[18]);
3227   if (YNotNil(keySymbols[19]))
3228     gistA.t.height= YGetReal(keySymbols[19])*ONE_POINT;
3229   if (YNotNil(keySymbols[20]))
3230     gistA.t.orient= YGetInteger(keySymbols[20]);
3231   if (YNotNil(keySymbols[21]))
3232     GetJustify(keySymbols[21]);
3233   if (YNotNil(keySymbols[22]))
3234     gistA.t.opaque= (YGetInteger(keySymbols[22])!=0);
3235 
3236   if (!gistA.t.orient) {
3237     gistA.t.orient= TX_RIGHT;
3238   } else {
3239     if (gistA.t.orient==1) gistA.t.orient= TX_UP;
3240     else if (gistA.t.orient==2) gistA.t.orient= TX_LEFT;
3241     else if (gistA.t.orient==3) gistA.t.orient= TX_DOWN;
3242     else {
3243       gistA.t.orient= TX_RIGHT;
3244       YError("orient= keyword must be 0, 1, 2, or 3");
3245     }
3246   }
3247 
3248   /* GdVectors properties */
3249   if (YNotNil(keySymbols[23]))
3250     gistA.vect.hollow= (YGetInteger(keySymbols[23])!=0);
3251   if (YNotNil(keySymbols[24]))
3252     gistA.vect.aspect= YGetReal(keySymbols[24]);
3253 
3254   if (YNotNil(keySymbols[25])) {  /* region */
3255     if (type<4 || type>7)
3256       YError("region= in pledit allowed only for plm, plf, plv, plc");
3257     gistD.region= (int)YGetInteger(keySymbols[25]);
3258   }
3259 
3260   if (YNotNil(keySymbols[26])) {  /* boundary */
3261     if (type!=4) YError("boundary= in pledit allowed only for plm");
3262     gistD.boundary= (YGetInteger(keySymbols[26])!=0);
3263   }
3264 
3265   if (YNotNil(keySymbols[27])) {  /* levs */
3266     double *levels;
3267     long nLevels= 0;
3268     if (type!=7) YError("levs= in pledit allowed only for plc");
3269     levels= Get1Ddouble(keySymbols[27], &nLevels);
3270     if (!levels)
3271       YError("pledit cannot recompute default contour levels");
3272     levels= CopyLevels(levels, nLevels);
3273     /* WARNING --
3274        this is a critical code section, since until GdEdit successfully
3275        completes, Gist owns a pointer to the freed levels -- no way to
3276        gracefully avoid this without "knowing" more about guts of Gist's
3277        data structures than seem reasonable here... */
3278     p_free(gistD.levels);
3279     gistD.levels= levels;
3280     gistD.nLevels= nLevels;
3281     changes|= CHANGE_Z;
3282     resetLevs= 1;
3283   }
3284 
3285   if (YNotNil(keySymbols[28])) {  /* scale */
3286     if (type!=6) YError("scale= in pledit allowed only for plv");
3287     gistD.scale= YGetReal(keySymbols[28]);
3288   }
3289 
3290   if (YNotNil(keySymbols[29])) {  /* scalem */
3291     if (type!=6) YError("scalem= in pledit allowed only for plv");
3292     gistD.scale*= YGetReal(keySymbols[29]);
3293   }
3294 
3295   if (YNotNil(keySymbols[30])) {  /* dx */
3296     if (type!=3) YError("dx= in pledit allowed only for plt");
3297     gistD.x0+= YGetReal(keySymbols[30]);
3298   }
3299 
3300   if (YNotNil(keySymbols[31])) {  /* dy */
3301     if (type!=3) YError("dy= in pledit allowed only for plt");
3302     gistD.y0+= YGetReal(keySymbols[31]);
3303   }
3304 
3305   if (YNotNil(keySymbols[32]))
3306     gistA.e.type= YGetInteger(keySymbols[32])? L_SOLID : L_NONE;
3307   if (YNotNil(keySymbols[33]))
3308     gistA.e.color= YgetColor(keySymbols[33]);
3309   if (YNotNil(keySymbols[34]))
3310     gistA.e.width= YGetReal(keySymbols[34]);
3311 
3312   if (YNotNil(keySymbols[35])) {  /* inhibit */
3313     if (type!=4) YError("inhibit= in pledit allowed only for plm");
3314     gistD.inhibit= (int)YGetInteger(keySymbols[35]);
3315   }
3316 
3317   if (legend) {
3318     /* Some giggery-pokery necessary to get the old legend deleted properly,
3319        and the new legend allocated properly, so that Gist will delete it
3320        correctly when the graphical element is deleted.  */
3321     char *oldleg= gistD.legend;
3322     gistD.legend= p_malloc(strlen(legend)+1);
3323     strcpy(gistD.legend, legend);
3324     legend= oldleg;
3325   }
3326   GdEdit(changes);
3327   if (legend) p_free(legend);
3328   if (resetLevs) tmpLevels= 0;
3329   Drop(nArgs);
3330 }
3331 
3332 #undef N_KEYWORDS
3333 #define N_KEYWORDS 29
3334 static char *dfltKeys[N_KEYWORDS+1]= {
3335   "color", "type", "width",
3336   "marks", "mcolor", "marker", "msize", "mspace", "mphase",
3337   "rays", "arrowl", "arroww", "rspace", "rphase",
3338   "font", "height", "orient", "justify", "opaque",
3339   "hollow", "aspect", "dpi", "style", "legends", "palette", "maxcolors",
3340   "edges", "ecolor", "ewidth", 0 };
3341 
Y_pldefault(int nArgs)3342 void Y_pldefault(int nArgs)
3343 {
3344   Symbol *keySymbols[N_KEYWORDS];
3345   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, dfltKeys, keySymbols);
3346 
3347   if (stack<=sp) YError("pldefault takes no non-keyword arguments");
3348 
3349   /* retrieve all default settings */
3350   GhGetLines();
3351   GhGetMesh();
3352   GhGetVectors();
3353   GhGetText();
3354 
3355   if (YNotNil(keySymbols[0]))
3356     gistA.l.color= gistA.m.color= gistA.f.color=
3357       gistA.t.color= gistA.e.color= YgetColor(keySymbols[0]);
3358   if (YNotNil(keySymbols[1]))
3359     gistA.l.type= GetLineType(keySymbols[1]);
3360   if (YNotNil(keySymbols[2]))
3361     gistA.l.width= YGetReal(keySymbols[2]);
3362   if (YNotNil(keySymbols[3]))
3363     gistA.dl.marks= (YGetInteger(keySymbols[3])!=0);
3364   if (YNotNil(keySymbols[4]))
3365     gistA.m.color= YgetColor(keySymbols[4]);
3366   if (YNotNil(keySymbols[5]))
3367     gistA.m.type= (int)YGetInteger(keySymbols[5]);
3368   if (YNotNil(keySymbols[6]))
3369     gistA.m.size= YGetReal(keySymbols[6]);
3370   if (YNotNil(keySymbols[7]))
3371     gistA.dl.mSpace= YGetReal(keySymbols[7]);
3372   if (YNotNil(keySymbols[8]))
3373     gistA.dl.mPhase= YGetReal(keySymbols[8]);
3374   if (YNotNil(keySymbols[9]))
3375     gistA.dl.rays= (YGetInteger(keySymbols[9])!=0);
3376   if (YNotNil(keySymbols[10]))
3377     gistA.dl.arrowL= YGetReal(keySymbols[10]);
3378   if (YNotNil(keySymbols[11]))
3379     gistA.dl.arrowW= YGetReal(keySymbols[11]);
3380   if (YNotNil(keySymbols[12]))
3381     gistA.dl.rSpace= YGetReal(keySymbols[12]);
3382   if (YNotNil(keySymbols[13]))
3383     gistA.dl.rPhase= YGetReal(keySymbols[13]);
3384   if (YNotNil(keySymbols[14]))
3385     gistA.t.font= GetFont(keySymbols[14]);
3386   if (YNotNil(keySymbols[15]))
3387     gistA.t.height= YGetReal(keySymbols[15])*ONE_POINT;
3388   if (YNotNil(keySymbols[16]))
3389     gistA.t.orient= YGetInteger(keySymbols[16]);
3390   if (YNotNil(keySymbols[17]))
3391     GetJustify(keySymbols[17]);
3392   if (YNotNil(keySymbols[18]))
3393     gistA.t.opaque= (YGetInteger(keySymbols[18])!=0);
3394   if (YNotNil(keySymbols[19]))
3395     gistA.vect.hollow= (YGetInteger(keySymbols[19])!=0);
3396   if (YNotNil(keySymbols[20]))
3397     gistA.vect.aspect= YGetReal(keySymbols[20]);
3398 
3399   if (!gistA.t.orient) {
3400     gistA.t.orient= TX_RIGHT;
3401   } else {
3402     if (gistA.t.orient==1) gistA.t.orient= TX_UP;
3403     else if (gistA.t.orient==2) gistA.t.orient= TX_LEFT;
3404     else if (gistA.t.orient==3) gistA.t.orient= TX_DOWN;
3405     else {
3406       gistA.t.orient= TX_RIGHT;
3407       YError("orient= keyword must be 0, 1, 2, or 3");
3408     }
3409   }
3410 
3411   if (YNotNil(keySymbols[21])) {
3412     int dpi= YGetInteger(keySymbols[21]);
3413     /*if (dpi!=75 && dpi!=100)
3414       YError("dpi=75 or dpi=100 are only legal values");*/
3415     if (dpi<25) dpi = 25;
3416     else if (dpi>2400) dpi = 2400;
3417     defaultDPI= dpi;
3418   }
3419   if (YNotNil(keySymbols[22])) {
3420     char *style= defaultStyle;
3421     defaultStyle= 0;
3422     p_free(style);
3423     style= YGetString(keySymbols[22]);
3424     if (style && style[0]) defaultStyle= p_strcpy(style);
3425   }
3426   if (YNotNil(keySymbols[23]))
3427     /* legends= keyword -- turn on/off legend dumping to hcp file */
3428     defaultLegends= (YGetInteger(keySymbols[23])!=0);
3429   if (keySymbols[24]) {
3430     char *name= defaultPalette;
3431     defaultPalette= 0;
3432     p_free(name);
3433     if (YNotNil(keySymbols[24]))
3434       defaultPalette= p_strcpy(YGetString(keySymbols[24]));
3435   }
3436   if (YNotNil(keySymbols[25]))
3437     maxColors= YGetInteger(keySymbols[25]);
3438 
3439   if (YNotNil(keySymbols[26]))
3440     gistA.e.type= YGetInteger(keySymbols[26])? L_SOLID : L_NONE;
3441   if (YNotNil(keySymbols[27]))
3442     gistA.e.color= YgetColor(keySymbols[27]);
3443   if (YNotNil(keySymbols[28]))
3444     gistA.e.width= YGetReal(keySymbols[28]);
3445 
3446   /* store all default settings */
3447   GhSetLines();
3448   GhSetMesh();
3449   GhSetVectors();
3450   GhSetText();
3451   GhSetFill();
3452 }
3453 
3454 #undef N_KEYWORDS
3455 #define N_KEYWORDS 6
3456 static char *gridKeys[N_KEYWORDS+1]= {
3457   "color", "type", "width", "base60", "degrees", "hhmm", 0 };
3458 
Y_gridxy(int nArgs)3459 void Y_gridxy(int nArgs)
3460 {
3461   Symbol *keySymbols[N_KEYWORDS];
3462   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, gridKeys, keySymbols);
3463   int xgrid= 0;
3464   int ygrid= 0;
3465   int iPass= 0;
3466   int ticks = 0;
3467 
3468   while (stack<=sp) {
3469     if (!stack->ops) { stack+= 2; continue; }
3470 
3471     if (iPass==0) {
3472       if (YNotNil(stack)) xgrid= (int)YGetInteger(stack);
3473     } else if (iPass==1) {
3474       if (YNotNil(stack)) ygrid= (int)YGetInteger(stack);
3475     } else {
3476       YError("gridxy takes at most two non-keyword arguments");
3477     }
3478 
3479     iPass++;
3480     stack++;
3481   }
3482 
3483   /* If a single argument is given, use it for both xgrid and ygrid */
3484   if (iPass==1) ygrid= xgrid;
3485 
3486   CheckDefaultWindow();
3487 
3488   if (YNotNil(keySymbols[0]))
3489     gistD.ticks.horiz.gridStyle.color=
3490       gistD.ticks.vert.gridStyle.color= YgetColor(keySymbols[0]);
3491   if (YNotNil(keySymbols[1]))
3492     gistD.ticks.horiz.gridStyle.type=
3493       gistD.ticks.vert.gridStyle.type= GetLineType(keySymbols[1]);
3494   if (YNotNil(keySymbols[2]))
3495     gistD.ticks.horiz.gridStyle.width=
3496       gistD.ticks.vert.gridStyle.width= YGetReal(keySymbols[2]);
3497   if (YNotNil(keySymbols[3]))
3498     ticks |= 1 | ((YGetInteger(keySymbols[3]) & 3) << 1);
3499   if (YNotNil(keySymbols[4]))
3500     ticks |= 1 | ((YGetInteger(keySymbols[4]) & 3) << 3);
3501   if (YNotNil(keySymbols[5]))
3502     ticks |= 1 | ((YGetInteger(keySymbols[5]) & 3) << 5);
3503   if (ticks&1) {
3504     if (ticks&0x2a) {
3505       if (ticks&0x2) GdAltTick(&Base60Ticks,0,0,0);
3506       else if (ticks&0x8) GdAltTick(&Base60Ticks,&DegreeLabels,0,0);
3507       else GdAltTick(&Base60Ticks,&HourLabels,0,0);
3508       gistD.ticks.horiz.flags |= ALT_TICK;
3509       if (ticks&0x2) gistD.ticks.horiz.flags &= ~ALT_LABEL;
3510       else gistD.ticks.horiz.flags |= ALT_LABEL;
3511     } else {
3512       gistD.ticks.horiz.flags &= ~(ALT_TICK|ALT_LABEL);
3513     }
3514     if (ticks&0x54) {
3515       if (ticks&0x4) GdAltTick(0,0,&Base60Ticks,0);
3516       else if (ticks&0x10) GdAltTick(0,0,&Base60Ticks,&DegreeLabels);
3517       else GdAltTick(0,0,&Base60Ticks,&HourLabels);
3518       gistD.ticks.vert.flags |= ALT_TICK;
3519       if (ticks&0x4) gistD.ticks.vert.flags &= ~ALT_LABEL;
3520       else gistD.ticks.vert.flags |= ALT_LABEL;
3521     } else {
3522       gistD.ticks.vert.flags &= ~(ALT_TICK|ALT_LABEL);
3523     }
3524   }
3525 
3526   if (iPass>0) {
3527     gistD.ticks.horiz.flags&= ~(GRID_F|GRID_O);
3528     if (xgrid==1)
3529       gistD.ticks.horiz.flags|= GRID_F;
3530     else if (xgrid==2)
3531       gistD.ticks.horiz.flags|= GRID_O;
3532     if (xgrid&0x200) {
3533       gistD.ticks.horiz.flags= (xgrid&0x1ff);
3534       gistD.ticks.frame= (xgrid&0x400)!=0;
3535     }
3536 
3537     gistD.ticks.vert.flags&= ~(GRID_F|GRID_O);
3538     if (ygrid&1)
3539       gistD.ticks.vert.flags|= GRID_F;
3540     else if (ygrid&2)
3541       gistD.ticks.vert.flags|= GRID_O;
3542     if (ygrid&0x200) {
3543       gistD.ticks.vert.flags= (ygrid&0x1ff);
3544       gistD.ticks.frame= (ygrid&0x400)!=0;
3545     }
3546   }
3547 
3548   GdSetPort();
3549 }
3550 
3551 /*--------------------------------------------------------------------------*/
3552 
FreeReference(void * obj)3553 static void FreeReference(void *obj)
3554 {
3555   Array *array= obj? Pointee(obj) : 0;
3556   Unref(array);
3557 }
3558 
3559 /* defined in task.c */
3560 extern void (*CleanUpForExit)(void);
3561 
3562 static void (*OtherCleanUp)(void)= 0;
3563 static void CleanUpGraphics(void);
CleanUpGraphics(void)3564 static void CleanUpGraphics(void)
3565 {
3566   int n;
3567   if (hcpDefault) GpKillEngine(hcpDefault);
3568   for (n=GH_NDEVS-1 ; n>=0 ; n--) {
3569     if (ghDevices[n].display) GpKillEngine(ghDevices[n].display);
3570     if (ghDevices[n].hcp) GpKillEngine(ghDevices[n].hcp);
3571   }
3572   if (OtherCleanUp) OtherCleanUp();
3573 }
3574 
3575 extern void yg_before_wait(void);
3576 void
yg_before_wait(void)3577 yg_before_wait(void)
3578 {
3579   GhBeforeWait();
3580 }
3581 
3582 void
Y_set_gpath(int argc)3583 Y_set_gpath(int argc)
3584 {
3585   char *p = ((argc==1) && YNotNil(sp))? YGetString(sp) : 0;
3586   if (argc > 1) YError("set_gpath accepts only one argument");
3587   if (!CalledAsSubroutine()) {
3588     Array *a = PushDataBlock(NewArray(&stringStruct, (Dimension *)0));
3589     a->value.q[0] = p_strcpy(g_set_path((char*)0));
3590   }
3591   if (p) g_set_path(p);
3592 }
3593 
Y__pl_init(int nArgs)3594 void Y__pl_init(int nArgs)
3595 {
3596 #ifndef NO_XLIB
3597   g_initializer(&ym_argc, ym_argv);
3598 #else
3599   extern char *g_argv0;
3600   g_argv0 = ym_argv? ym_argv[0] : 0;
3601 #endif
3602 
3603   /* Install routine to kill graphics engines when Yorick quits.  */
3604   OtherCleanUp= CleanUpForExit;
3605   CleanUpForExit= &CleanUpGraphics;
3606 
3607   /* Additionally, set up so that Gist Drauing structures actually own
3608      a use of mesh-sized Yorick Arrays.  This allows one or more
3609      Drauings to share these potentially large objects.  */
3610   GdFree= &FreeReference;
3611 
3612   /* Install Yorick's best guess at a GISTPATH.  The GISTPATH
3613      environment variable, if present, will be used; otherwise the
3614      argument to this function, if non-nil, will be used; otherwise,
3615      the default compiled into libgist.a will be used.  */
3616   if (!p_getenv("GISTPATH") && nArgs==1 && YNotNil(sp))
3617     gistPathDefault= p_strcpy(YGetString(sp));
3618   GhSetXHandler((void (*)(char *))&YError);
3619 
3620   /* Set up parser to pass string equivalents of arguments to the
3621      plotting functions for use in the construction of default legends.
3622      NB-- The parser can only quine functions invoked as subroutines,
3623           so all quined functions must check CalledAsSubroutine().  */
3624   YpQuine("plg", 2);
3625   YpQuine("plm", 3);
3626   YpQuine("plc", 4);
3627   YpQuine("plv", 4);
3628   YpQuine("plf", 4);
3629   YpQuine("pli", 1);
3630   /* plt does not use legends */
3631   YpQuine("pldj", 4);
3632 
3633   /* Default is to put occasional markers on curves.  */
3634   GhGetLines();
3635   gistA.dl.marks= 1;
3636   GhSetLines();
3637 
3638   /* Default text is 14 point Helvetica.  */
3639   GhGetText();
3640   gistA.t.font= T_HELVETICA;
3641   gistA.t.height= 14.0*ONE_POINT;
3642   GhSetText();
3643 }
3644 
Y_keybd_focus(int nArgs)3645 void Y_keybd_focus(int nArgs)
3646 {
3647   /* Set the input hint to false, meaning that Yorick graphics
3648    * windows do not ever want keyboard focus.  This is not supposed
3649    * to affect mouse input events, which are wanted.  */
3650 #ifndef NO_XLIB
3651   if (nArgs==1) gist_input_hint= YGetInteger(sp);
3652 #endif
3653 }
3654 
3655 /*--------------------------------------------------------------------------*/
3656 
3657 #undef N_KEYWORDS
3658 #define N_KEYWORDS 3
3659 static char *bsKeys[N_KEYWORDS+1]= { "top", "cmin", "cmax", 0 };
3660 
Y_bytscl(int nArgs)3661 void Y_bytscl(int nArgs)
3662 {
3663   Symbol *keySymbols[N_KEYWORDS];
3664   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, bsKeys, keySymbols);
3665   double *z, zmin, zmax, scale, offset, zz;
3666   Operand op;
3667   Array *array;
3668   GpColor *zc;
3669   long i;
3670 
3671   z= 0;
3672   while (stack<=sp) {
3673     if (!stack->ops) { stack+=2; continue; }
3674     if (z) { z= 0; break; }
3675     stack->ops->FormOperand(stack, &op);
3676     if (op.ops->promoteID>T_DOUBLE)
3677       YError("bytscl argument must be convertable to type double");
3678     op.ops->ToDouble(&op);
3679     z= op.value;
3680     stack++;
3681   }
3682   if (!z) YError("bytscl takes exactly one non-keyword argument");
3683 
3684   GrabByteScale(keySymbols, &scale, &offset, &zmin, &zmax,
3685                 z, (int *)0, 0, op.type.number+1, 2L, 1);
3686 
3687   array= PushDataBlock(NewArray(&charStruct, op.type.dims));
3688   zc= (GpColor *)array->value.c;
3689 
3690   for (i=0 ; i<op.type.number ; i++) {
3691     zz= z[i];
3692     if (zz<zmin) zz= zmin;
3693     else if (zz>zmax) zz= zmax;
3694     zc[i]= (int)((zz-offset)*scale);
3695   }
3696 }
3697 
3698 /*--------------------------------------------------------------------------*/
3699 
3700 #undef N_KEYWORDS
3701 #define N_KEYWORDS 2
3702 static char *cntrKeys[N_KEYWORDS+1]= { "triangle", "region", 0 };
3703 
Y_contour(int nArgs)3704 void Y_contour(int nArgs)
3705 {
3706   Symbol *keySymbols[N_KEYWORDS];
3707   Symbol *stack= YGetKeywords(sp-nArgs+1, nArgs, cntrKeys, keySymbols);
3708   long iMax= 0, jMax= 0;
3709   double *z= 0;
3710   GaQuadMesh mesh;
3711   long xci= 0, yci= 0;
3712   Symbol *xs= 0 , *ys= 0;
3713   double *levs= 0;
3714   int two_levels= 0;
3715   Dimension *dims;
3716   Array *array;
3717   double *xcp, *ycp;
3718   long *np, nparts, ntotal;
3719   int region= 0;
3720   int iPass= 0;
3721 
3722   while (stack<=sp && iPass<4) {
3723     if (!stack->ops) { stack+= 2; continue; }
3724 
3725     if (iPass==0) {
3726       yci= YGet_Ref(stack);
3727       ys= stack;
3728     } else if (iPass==1) {
3729       xci= YGet_Ref(stack);
3730       xs= stack;
3731     } else if (iPass==2) {
3732       levs= YGet_D(stack, 0, &dims);
3733       if (dims && (dims->number>2 || dims->next))
3734         YError("contour levs must be either single value or value pair");
3735       if (dims && dims->number==2) two_levels= 1;
3736     } else if (iPass==3) {
3737       z= Get2Ddouble(stack, &jMax, &iMax);
3738     }
3739 
3740     iPass++;
3741     stack++;
3742   }
3743   if (iPass<4) YError("contour needs at least six arguments");
3744   stack= GrabMesh(stack, keySymbols[0], &mesh,
3745                   (char **)0, (char **)0, (char **)0, 1);
3746   while (stack<=sp) {
3747     if (!stack->ops) stack+= 2;
3748     else YError("contour takes at most seven non-keyword arguments");
3749   }
3750   if (mesh.iMax!=iMax || mesh.jMax!=jMax)
3751     YError("z array must have same dimensions as mesh in contour");
3752   if (YNotNil(keySymbols[1]))
3753     region= (int)YGetInteger(keySymbols[1]);
3754 
3755   if (!mesh.triangle) {
3756     /* provide a temporary triangle array if none supplied */
3757     short *triangle;
3758     long ijMax= iMax*jMax;
3759     dims= tmpDims;
3760     tmpDims= 0;
3761     FreeDimension(dims);
3762     tmpDims= NewDimension(ijMax, 1L, (Dimension *)0);
3763     triangle= ((Array*)PushDataBlock(NewArray(&shortStruct,
3764                                               tmpDims)))->value.s;
3765     mesh.triangle= triangle;
3766     while (ijMax--) *(triangle++)= 0;
3767     CheckStack(1);
3768   }
3769 
3770   /* initialize trace, counting ntotal and nparts */
3771   ntotal= two_levels? GcInit2(&mesh,region,z,levs,30L,&nparts) :
3772     GcInit1(&mesh,region,z,levs[0],&nparts);
3773 
3774   if (!ntotal) {
3775     /* handle case of no points on contour */
3776     PushDataBlock(RefNC(&nilDB));
3777     YPut_Result(sp, yci);
3778     YPut_Result(sp, xci);
3779     return;
3780   }
3781 
3782   /* stuff x and y arrays for results onto stack over output symbols */
3783   dims= tmpDims;
3784   tmpDims= 0;
3785   FreeDimension(dims);
3786   tmpDims= dims= NewDimension(ntotal, 1L, (Dimension *)0);
3787   array= NewArray(&doubleStruct, tmpDims);
3788   ys->value.db= (DataBlock *)array;
3789   ys->ops= &dataBlockSym;
3790   ycp= array->value.d;
3791   array= NewArray(&doubleStruct, tmpDims);
3792   xs->value.db= (DataBlock *)array;
3793   xs->ops= &dataBlockSym;
3794   xcp= array->value.d;
3795 
3796   /* return list goes on top of stack */
3797   tmpDims= 0;
3798   FreeDimension(dims);
3799   tmpDims= NewDimension(nparts, 1L, (Dimension *)0);
3800   array= PushDataBlock(NewArray(&longStruct, tmpDims));
3801   np= array->value.l;
3802 
3803   if (GcTrace(np, xcp, ycp)!=ntotal) YError("GcTrace failed in contour");
3804 
3805   /* move results from stack back to output symbols */
3806   YPut_Result(ys, yci);
3807   YPut_Result(xs, xci);
3808 }
3809 
Y_mesh_loc(int nArgs)3810 void Y_mesh_loc(int nArgs)
3811 {
3812   Symbol *stack= sp-nArgs+1;
3813   double *x0= 0, *y0= 0;
3814   Operand xop, yop;
3815   Array *result;
3816   long i, n, *zone;
3817   GaQuadMesh mesh;
3818   int iPass= 0;
3819   Dimension *dims;
3820   long ix0, j, ijx, *bndy, nbndy;
3821 
3822   mesh.x= mesh.y= 0;
3823   mesh.reg= 0;
3824   mesh.iMax= mesh.jMax= 0;
3825   mesh.triangle= 0;
3826 
3827   if (nArgs<2) YError("mesh_loc requires at least two arguments");
3828   while (stack<=sp) {
3829     if (!stack->ops) YError("mesh_loc takes no keyword arguments");
3830 
3831     if (iPass==0) {
3832       Dimension *dims;
3833       y0= YGet_D(stack, 0, &dims);
3834       stack->ops->FormOperand(stack, &yop);
3835     } else if (iPass==1) {
3836       Dimension *dims;
3837       x0= YGet_D(stack, 0, &dims);
3838       stack->ops->FormOperand(stack, &xop);
3839     } else if (iPass==2) mesh.y= Get2Ddouble(stack, &mesh.jMax, &mesh.iMax);
3840     else if (iPass==3) mesh.x= Get2Ddouble(stack, &mesh.jMax, &mesh.iMax);
3841     else if (iPass==4) mesh.reg= Get2Dint(stack, &mesh.jMax, &mesh.iMax);
3842     else YError("mesh_loc takes at most five arguments");
3843 
3844     iPass++;
3845     stack++;
3846   }
3847 
3848   if ((mesh.x!=0)^(mesh.y!=0))
3849     YError("both y and x arrays must be specified for a mesh");
3850 
3851   if (!mesh.x) {
3852     /* neither y nor x have been specified -- use defaults */
3853     if (!xMesh)
3854       YError("no default mesh exists to define y and x -- use plmesh");
3855     if (mesh.reg && (iMesh!=mesh.iMax || jMesh!=mesh.jMax))
3856       YError("ireg must have same dimensions as default mesh");
3857     mesh.iMax= iMesh;
3858     mesh.jMax= jMesh;
3859     mesh.x= xMesh;
3860     mesh.y= yMesh;
3861 
3862   } else {
3863     /* both y and x have been specified -- copy them for Gist */
3864     if (mesh.iMax<2 || mesh.jMax<2)
3865       YError("a mesh must have dimensions of at least 2-by-2");
3866   }
3867 
3868   if (!mesh.reg && mesh.x==xMesh) mesh.reg= regMesh;
3869 
3870   if (BinaryConform(&xop, &yop)&4) YError("x0 and y0 not conformable");
3871   n= TotalNumber(tmpDims);
3872   if (n>1) {
3873     result= PushDataBlock(NewArray(&longStruct, tmpDims));
3874     zone= result->value.l;
3875   } else {
3876     PushLongValue(0);
3877     zone= &sp->value.l;
3878   }
3879 
3880   /* get fastest varying dimension in (x0,y0) */
3881   dims= tmpDims;
3882   ix0= 1;
3883   while (dims && dims->next) {
3884     dims= dims->next;
3885     if (dims->number>1) ix0= dims->number;
3886   }
3887 
3888   ijx= mesh.iMax*mesh.jMax;
3889   if (mesh.reg) {
3890     long i0= 0;
3891     for (j=mesh.iMax+1 ; j<ijx ; j++) {
3892       if ((++i0)==mesh.iMax) { i0= 1; j++; }
3893       if (mesh.reg[j]) break;
3894     }
3895     if (j>=ijx) j= -1;
3896   } else {
3897     j= mesh.iMax+1;
3898   }
3899   bndy= 0;
3900   nbndy= 0;
3901   for (i=0 ; i<n ; i++) {
3902     if (i && !(i%ix0)) j= zone[i-ix0]-1;
3903     j= QuickMeshZone(x0[i], y0[i], mesh.x, mesh.y, mesh.reg,
3904                      mesh.iMax, ijx, j, bndy, nbndy);
3905     if (!bndy && j<0) {
3906       bndy= BuildMeshBndy(mesh.x, mesh.y, mesh.reg, mesh.iMax, ijx, &nbndy);
3907       j= QuickMeshZone(x0[i], y0[i], mesh.x, mesh.y, mesh.reg,
3908                        mesh.iMax, ijx, j, bndy, nbndy);
3909     }
3910     zone[i]= j+1;
3911   }
3912   if (bndy) Drop(1);
3913 }
3914 
3915 /*--------------------------------------------------------------------------*/
3916 
3917 #ifndef NO_MOUSE
3918 static int MouseCallBack(Engine *engine, int system,
3919                          int release, GpReal x, GpReal y,
3920                          int butmod, GpReal xn, GpReal yn);
3921 static char *defaultPrompts[2]= {
3922   "<Click mouse at point>", "<Press, drag, and release mouse>" };
3923 static Array *mouse_array = 0;
3924 #endif
3925 
Y_mouse(int nArgs)3926 void Y_mouse(int nArgs)
3927 {
3928 #ifdef DISPLAY_MOUSE
3929   Symbol *stack= sp-nArgs+1;
3930   char *prompt= 0;
3931   int system= -1, style= 0, iPass= 0;
3932   int n= GhGetPlotter();
3933 
3934   if (n<0 || !ghDevices[n].display)
3935     YError("no current graphics window for mouse function");
3936 
3937   while (stack<=sp) {
3938     if (!stack->ops) YError("mouse function takes no keyword arguments");
3939     if (iPass==0)
3940       system= YNotNil(stack)? YGetInteger(stack) : -1;
3941     else if (iPass==1)
3942       style= YNotNil(stack)? YGetInteger(stack) : 0;
3943     else if (iPass==2)
3944       prompt= YNotNil(stack)? YGetString(stack) : 0;
3945     else
3946       YError("mouse function takes at most three arguments");
3947     iPass++;
3948     stack++;
3949   }
3950 
3951   /* GhWaitDisplay();   otherwise can lock up ?? */
3952   GhBeforeWait();    /* be sure display is current */
3953   if (!prompt) YPrompt(defaultPrompts[style!=0]);
3954   else if (prompt[0]) YPrompt(prompt);
3955   if (!prompt || prompt[0]) YPrompt("\n");
3956 
3957   if (DISPLAY_MOUSE(ghDevices[n].display, style, system, &MouseCallBack)) {
3958     PushDataBlock(RefNC(&nilDB));
3959   } else {
3960     mouse_array=
3961       PushDataBlock(NewArray(&doubleStruct,
3962                              NewDimension(11L, 1L, (Dimension *)0)));
3963     mouse_array->type.dims->references--;
3964     for (n=0 ; n<11 ; n++) mouse_array->value.d[n]= 0.0;
3965     if (yg_pc_resume) {
3966       yg_got_expose();
3967       YError("mouse while already waiting or suspended");
3968     } else {
3969       yg_blocking = 1;
3970       yg_pc_resume = ym_suspend();
3971     }
3972   }
3973 #else
3974   YError("no mouse function in this version of Yorick");
3975 #endif
3976 }
3977 
3978 #ifndef NO_MOUSE
MouseCallBack(Engine * engine,int system,int release,GpReal x,GpReal y,int butmod,GpReal xn,GpReal yn)3979 static int MouseCallBack(Engine *engine, int system,
3980                          int release, GpReal x, GpReal y,
3981                          int butmod, GpReal xn, GpReal yn)
3982 {
3983   int n= GhGetPlotter();
3984   if (n<0 || ghDevices[n].display!=engine) {
3985     mouse_array = 0;
3986     yg_got_expose();
3987     return 1;
3988   } else if (!mouse_array || sp->ops!=&dataBlockSym ||
3989              mouse_array != (Array*)sp->value.db ||
3990              mouse_array->type.base != &doubleStruct ||
3991              mouse_array->type.number != 11) {
3992     mouse_array = 0;
3993     yg_got_expose();
3994     return 1;
3995   } else if (release == -1) {
3996     mouse_array = 0;
3997     yg_got_expose();
3998     return 1;
3999   }
4000   if (!release) {
4001     mouse_array->value.d[8]= (double)system;
4002     mouse_array->value.d[9]= (double)butmod;
4003     mouse_array->value.d[0]= x;
4004     mouse_array->value.d[1]= y;
4005     mouse_array->value.d[4]= xn;
4006     mouse_array->value.d[5]= yn;
4007   } else {
4008     mouse_array->value.d[10]= (double)butmod;
4009     mouse_array->value.d[2]= x;
4010     mouse_array->value.d[3]= y;
4011     mouse_array->value.d[6]= xn;
4012     mouse_array->value.d[7]= yn;
4013     mouse_array = 0;
4014   }
4015   return 0;
4016 }
4017 #endif
4018 
4019 /*--------------------------------------------------------------------------*/
4020 
YCurrentPlotter(void)4021 int YCurrentPlotter(void)
4022 {
4023   return GhGetPlotter();
4024 }
4025 
4026 /*--------------------------------------------------------------------------*/
4027 
Y_current_window(int nArgs)4028 void Y_current_window(int nArgs)
4029 {
4030   PushIntValue(GhGetPlotter());
4031 }
4032 
4033 void
Y_pause(int nArgs)4034 Y_pause(int nArgs)
4035 {
4036   long timeout;
4037   if (nArgs!=1) YError("pause requires exactly one argument");
4038   timeout = YGetInteger(sp);
4039   if (timeout<0) timeout = 0;
4040   Drop(nArgs);
4041   sp->ops = &intScalar;
4042   sp->value.i = 0;
4043   if (yg_pc_resume) {
4044     yg_got_expose();
4045     YError("pause while already waiting or suspended");
4046   } else {
4047     p_set_alarm(0.001*timeout, yg_alarm, (sp-spBottom)+(char*)0);
4048     yg_blocking = 2;
4049     yg_pc_resume = ym_suspend();
4050   }
4051 }
4052 
4053 /* ARGSUSED */
4054 static void
yg_alarm(void * context)4055 yg_alarm(void *context)
4056 {
4057   if (yg_blocking != 2) return;
4058   if (sp-spBottom==((char*)context-(char*)0) &&
4059       sp->ops==&intScalar && sp->value.i==0)
4060     sp->value.i = 1;
4061   yg_got_expose();
4062 }
4063 
4064 void
Y_rgb_read(int nArgs)4065 Y_rgb_read(int nArgs)
4066 {
4067   int n = GhGetPlotter();
4068   long nx, ny;
4069   Array *result;
4070   Dimension *dims = tmpDims;
4071   if (nArgs>1) YError("rgb_read takes no more than one argument");
4072   if (nArgs==1 && YNotNil(sp)) n = YGetInteger(sp);
4073   if (n<0 || n>=GH_NDEVS || !ghDevices[n].display ||
4074       RGB_READER(ghDevices[n].display, (GpColor*)0, &nx, &ny))
4075     YError("rgb_read(n_window) with no such n_window");
4076   tmpDims = 0;
4077   FreeDimension(dims);
4078   tmpDims = NewDimension(3L, 1L, (Dimension *)0);
4079   tmpDims = NewDimension(nx, 1L, tmpDims);
4080   tmpDims = NewDimension(ny, 1L, tmpDims);
4081   result = PushDataBlock(NewArray(&charStruct, tmpDims));
4082   RGB_READER(ghDevices[n].display, (GpColor*)result->value.c, &nx, &ny);
4083 }
4084 
4085 #ifdef NO_XLIB
4086 /* ARGSUSED */
4087 static int
my_rgb_read(Engine * eng,GpColor * rgb,long * nx,long * ny)4088 my_rgb_read(Engine *eng, GpColor *rgb, long *nx, long *ny)
4089 {
4090   YError("rgb_read impossible - no interactive graphics in this yorick");
4091   return 1;
4092 }
4093 #endif
4094 
4095 /*--------------------------------------------------------------------------*/
4096 
4097 void
Y_current_mouse(argc)4098 Y_current_mouse(argc)
4099 {
4100 #ifdef NO_XLIB
4101   PushDataBlock(RefNC(&nilDB));
4102 #else
4103   double x, y;
4104   int sys, win, target_win;
4105   Array *array;
4106   double *result;
4107 
4108   if (argc != 1) {
4109     YError("current_mouse takes exactly one, possibly nil, argument");
4110   }
4111   win = GhGetMouse(&sys, &x, &y);
4112   if (YNotNil(sp)) {
4113     target_win = (int)YGetInteger(sp);
4114   } else {
4115     target_win = win;
4116   }
4117   if (win < 0 || win != target_win) {
4118     PushDataBlock(RefNC(&nilDB));
4119   } else {
4120     array = PushDataBlock(NewArray(&doubleStruct,
4121 				   NewDimension(4L, 1L, (Dimension *)0)));
4122     --array->type.dims->references;
4123     result = array->value.d;
4124     result[0] = x;
4125     result[1] = y;
4126     result[2] = sys;
4127     result[3] = win;
4128   }
4129 #endif
4130 }
4131 
4132 /*--------------------------------------------------------------------------*/
4133