1 /* *************************************************************************
2    plotting.hpp  -  GDL routines for plotting
3    -------------------
4    begin                : July 22 2002
5    copyright            : (C) 2002 by Marc Schellens
6    email                : m_schellens@users.sf.net
7 ***************************************************************************/
8 
9 /* *************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #ifndef PLOTTING_HPP_
19 #define PLOTTING_HPP_
20 #define gdlPlot_Min(a, b) ((a) < (b) ? (a) : (b))
21 #define gdlPlot_Max(a, b) ((a) > (b) ? (a) : (b))
22 
23 
24 //To debug Affine 3D homogenous projections matrices.
25 //IDL define a matrix as  M[ncol,mrow] and print as such. However col_major and
26 //row_major refer to the math notation M[row,col] where row=dim(0) and col=dim(1).
27 //Matrices are stored COL Major in IDL/Fortran and ROW Major in C,C++ etc.
28 //so element at (i,j) is computed as  (j*dim0 + i) for ColMajor/IDL
29 //and (i*dim1 + j) for RowMajor/C
30 
31 #define TRACEMATRIX_C(var__)						\
32   {int dim0__=(var__)->Dim(0), dim1__=(var__)->Dim(1);			\
33     fprintf(stderr,"c matrix[%d,%d]\n",dim0__,dim1__);			\
34     for (int row=0; row < dim0__ ; row++)				\
35       {									\
36 	for (int col=0; col < dim1__-1; col++)				\
37           {								\
38             fprintf(stderr,"%g, ",(*var__)[row*dim1__ + col]);		\
39           }								\
40 	fprintf(stderr,"%g\n",(*var__)[row*dim1__ + dim1__ -1]);	\
41       }									\
42     fprintf(stderr,"\n");						\
43   }
44 //The following abbrevs should output the C matrix as IDL would do (ie,transposed):
45 #define TRACEMATRIX_IDL(var__)						\
46   {int dim0__=(var__)->Dim(0), dim1__=(var__)->Dim(1);			\
47     fprintf(stderr,"idl matrix[%d,%d]\n[",dim0__,dim1__);		\
48     for (int col=0; col < dim1__; col++)				\
49       {									\
50 	fprintf(stderr,"[");						\
51 	for (int row=0; row < dim0__; row++)				\
52           {								\
53             fprintf(stderr,"%g",(*var__)[row*dim1__ + col]);		\
54             if (row<dim0__-1) fprintf(stderr," ,");			\
55             else if (col<dim1__-1) fprintf(stderr," ],$\n"); else fprintf(stderr," ]]\n") ; \
56           }								\
57       }									\
58   }
59 
60 #include "envt.hpp"
61 #include "graphicsdevice.hpp"
62 #include "initsysvar.hpp"
63 
64 #ifdef USE_LIBPROJ
65 #include "projections.hpp"
66 #endif
67 
68 struct GDL_3DTRANSFORMDATA
69 {
70   DDoubleGDL* Matrix;
71   DDouble zValue;
72   int* code;
73   DDouble x0;
74   DDouble xs;
75   DDouble y0;
76   DDouble ys;
77   DDouble z0;
78   DDouble zs;
79   bool xlog;
80   bool ylog;
81   bool zlog;
82 };
83 
84 static GDL_3DTRANSFORMDATA Data3d;
85 
86 static int code012[3] = {0, 1, 2};
87 static int code102[3] = {1, 0, 2};
88 static int code120[3] = {1, 2, 0};
89 static int code210[3] = {2, 1, 0};
90 static int code201[3] = {2, 0, 1};
91 static int code021[3] = {0, 2, 1};
92 
93 enum ORIENTATION3D
94   {
95     NORMAL3D=0,
96     XY,
97     XZ,
98     YZ,
99     XZYZ,
100     XZXY
101   };
102 
103 enum PLOT_AXES_IDENTIFIERS
104 {
105  XAXIS=0,
106  YAXIS,
107  ZAXIS,
108  XAXIS2, //special identifiere for gdlAxis
109  YAXIS2,
110  ZAXIS2
111 };
112 
113 static const std::string axisName[6]={"X","Y","Z","X","Y","Z"};
114 
115 #define GDL_NONE -1
116 #define GDL_TICKFORMAT 0
117 #define GDL_TICKUNITS 1
118 #define GDL_TICKFORMAT_AND_UNITS 2
119   struct GDL_TICKDATA
120   {
121     GDLGStream *a;
122     bool isLog;
123     DDouble axisrange; //to circumvent plplot passing a non-zero value instead of strict 0.0
124     double nchars; //length of string *returned* after formatting. Can be non-integer.
125   };
126 
127   struct GDL_TICKNAMEDATA
128   {
129     GDLGStream *a;
130     bool isLog;
131     DDouble axisrange; //to circumvent plplot passing a non-zero value instead of strict 0.0
132     double nchars; //length of string *returned* after formatting. Can be non-integer.
133     SizeT counter;
134     SizeT nTickName;
135     DStringGDL* TickName;
136   };
137 
138   struct GDL_MULTIAXISTICKDATA
139   {
140     GDLGStream *a;
141     bool isLog;
142     DDouble axisrange; //to circumvent plplot passing a non-zero value instead of strict 0.0
143     double nchars; //length of string *returned* after formatting. Can be non-integer.
144     SizeT counter;
145     bool reset; //reset internal counter each time a new 'axis' command is issued
146     int what;
147     SizeT nTickFormat;
148     DDouble axismin;
149     DDouble axismax;
150     DStringGDL* TickFormat;
151     SizeT nTickUnits;
152     DStringGDL* TickUnits;
153     EnvT *e;
154   };
155 
156   typedef struct GDL_SAVEBOX {
157    bool initialized;
158     PLFLT wx1; //world coord of x min
159     PLFLT wx2;
160     PLFLT wy1;
161     PLFLT wy2;
162     PLFLT nx1;
163     PLFLT nx2;
164     PLFLT ny1;
165     PLFLT ny2;
166   } gdlSavebox ;
167 
168 namespace lib {
169 
170   using namespace std;
171 
172   // main plotting routine (all defined using the plotting_routine_call class)
173   void plot( EnvT* e);
174   void plot_io( EnvT* e);
175   void plot_oo( EnvT* e);
176   void plot_oi( EnvT* e);
177   void oplot( EnvT* e);
178   void plots( EnvT* e);
179   void surface( EnvT* e);
180   void shade_surf( EnvT* e);
181   void contour( EnvT* e);
182   void xyouts( EnvT* e);
183   void axis( EnvT* e);
184   void polyfill( EnvT* e);
185   void tv_image( EnvT* e);
186   void usersym( EnvT* e);
187   void set_shading( EnvT* e);
188 
189   // other plotting routines
190   void erase( EnvT* e);
191   void tvlct( EnvT* e);
192   void wshow( EnvT* e);
193   void wdelete( EnvT* e);
194   void wset( EnvT* e);
195   void window( EnvT* e);
196   void set_plot( EnvT* e);
197   BaseGDL* get_screen_size( EnvT* e);
198   void device( EnvT* e);
199   void cursor( EnvT* e);
200   void tvcrs( EnvT* e);
201   void empty(EnvT* e);
202   BaseGDL* format_axis_values(EnvT *e);
203   void scale3_pro(EnvT* e);
204   void t3d_pro( EnvT* e);
205 
206   BaseGDL* convert_coord( EnvT* e);
207 
208   // Map stuff
209   void get_mapset(bool &mapset);
210   void set_mapset(bool mapset);
211 #ifdef USE_LIBPROJ
212   void GDLgrProjectedPolygonPlot(GDLGStream * a, PROJTYPE ref, DStructGDL* map,
213 				 DDoubleGDL *lons, DDoubleGDL *lats, bool isRadians,
214 				 bool const doFill, DLongGDL *conn=NULL);
215 #endif
216   //3D conversions
217   void SelfTranspose3d(DDoubleGDL* me);
218   void SelfReset3d(DDoubleGDL* me);
219   void SelfTranslate3d(DDoubleGDL* me, DDouble *trans);
220   void SelfScale3d(DDoubleGDL* me, DDouble *scale);
221   void SelfRotate3d(DDoubleGDL* me, DDouble *rot);
222   void SelfPerspective3d(DDoubleGDL* me, DDouble zdist);
223   void SelfOblique3d(DDoubleGDL* me, DDouble dist, DDouble angle);
224   void SelfExch3d(DDoubleGDL* me, DLong code);
225   void gdl3dTo2dTransformContour(PLFLT x, PLFLT y, PLFLT *xt, PLFLT *yt, PLPointer data);
226   void gdl3dTo2dTransform(PLFLT x, PLFLT y, PLFLT *xt, PLFLT *yt, PLPointer data);
227   void gdlProject3dCoordinatesIn2d( DDoubleGDL* Matrix, DDoubleGDL *xVal, DDouble *sx,
228                                     DDoubleGDL *yVal, DDouble *sy, DDoubleGDL* zVal,
229                                     DDouble *sz, DDoubleGDL *xValou, DDoubleGDL *yValou);
230   DDoubleGDL* gdlComputePlplotRotationMatrix(DDouble az, DDouble alt, DDouble zValue, DDouble scale=1.0);
231   DDoubleGDL* gdlConvertT3DMatrixToPlplotRotationMatrix(DDouble zValue, DDouble &az, DDouble &alt,
232 							DDouble &ay, DDouble &scale, ORIENTATION3D &code);
233   DDoubleGDL* gdlGetScaledNormalizedT3DMatrix(DDoubleGDL* Matrix=NULL);
234   DDoubleGDL* gdlGetT3DMatrix();
235   void gdlNormed3dToWorld3d(DDoubleGDL *xVal, DDoubleGDL *yVal, DDoubleGDL* zVal,
236                             DDoubleGDL* xValou, DDoubleGDL *yValou, DDoubleGDL *zValou);
237   void gdl3dto2dProjectDDouble(DDoubleGDL* t3dMatrix, DDoubleGDL *xVal, DDoubleGDL *yVal,
238                                DDoubleGDL* zVal, DDoubleGDL *xValou, DDoubleGDL *yValou, int* code);
239   bool T3Denabled();
240 
241   class plotting_routine_call
242   {
243     // ensure execution of child-class destructors
~plotting_routine_call()244   public: virtual ~plotting_routine_call() {};
245 
246     // private fields
247   private: SizeT _nParam;
248   private: bool overplot;
249   private: bool isDB; //see below why commented.
250 
251     // common helper methods
nParam()252   protected: inline SizeT nParam() { return _nParam; }
253 
254     // prototypes for methods defining various steps
255   private: virtual bool handle_args(EnvT*) = 0; // return value = overplot
256   private: virtual void old_body(EnvT*, GDLGStream*) = 0;
257   private: virtual void call_plplot(EnvT*, GDLGStream*) = 0;
258   private: virtual void post_call(EnvT*, GDLGStream*) = 0;
259 
260     // all steps combined (virtual methods cannot be called from ctor)
call(EnvT * e,SizeT n_params_required)261   public: void call(EnvT* e, SizeT n_params_required)
262     {
263       // when !d.name == Null  we do nothing !
264       DString name = (*static_cast<DStringGDL*>(SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("NAME"), 0)))[0];
265       if (name == "NULL") return;
266 
267       _nParam = e->NParam(n_params_required);
268 
269       overplot = handle_args(e);
270 
271       GDLGStream* actStream = GraphicsDevice::GetDevice()->GetStream();
272       if (actStream == NULL) e->Throw("Unable to create window.");
273 
274       //ALL THE DoubleBuffering and Flush() code below introduces terrible slowness in remote X displays, as well as a lot of time lost
275       //for displays on the same server. They are completely removed now.
276       //      //double buffering kills the logic and operation of XOR modes. Use HasSafeDoubleBuffering() that tests this feature.)
277       //      isDB = actStream->HasSafeDoubleBuffering();
278       //      if (isDB) actStream->SetDoubleBuffering();
279 
280       if (name == "X" || name == "MAC" || name == "WIN" )  actStream->updatePageInfo(); //since window size can change
281 
282       old_body(e, actStream); // TODO: to be removed!
283       call_plplot(e, actStream);
284 
285       post_call(e, actStream);
286       // IDEM: SLOW
287       //      if (isDB) actStream->eop(); else actStream->flush();
288       //      if (isDB) actStream->UnSetDoubleBuffering();
289 
290       //this is absolutely necessary for widgets as for windows. However the virtual Update function
291       //i.e., calling  plstream::cmd(PLESC_EXPOSE, NULL) is very slow.
292       // See how to overload it by a faster function such as in GDLXStream::Update() .
293       actStream->Update();
294     }
295   };
296   void gdlDoRangeExtrema(DDoubleGDL *xVal, DDoubleGDL *yVal, DDouble &min, DDouble &max, DDouble xmin, DDouble xmax, bool doMinMax=FALSE, DDouble minVal=0, DDouble maxVal=0);
297   void draw_polyline(GDLGStream *a, DDoubleGDL *xVal, DDoubleGDL *yVal,
298 		     DDouble minVal, DDouble maxVal, bool doMinMax,
299 		     bool xLog, bool yLog, //end non-default values
300          DLong psym=0, bool useProjectionInfo=false, bool append=FALSE, DLongGDL *color=NULL);
301   //protect from (inverted, strange) axis log values
302   void gdlHandleUnwantedAxisValue(DDouble &min, DDouble &max, bool log);
303   void gdlSetGraphicsPenColorToBackground(GDLGStream *a);
304   void gdlLineStyle(GDLGStream *a, DLong style);
305   void gdlStoreAxisCRANGE(int axisId, DDouble Start, DDouble End, bool log);
306   void gdlStoreAxisSandWINDOW(GDLGStream* actStream, int axisId, DDouble Start, DDouble End, bool log=false);
307   void gdlGetAxisType(int axisId, bool &log);
308   void gdlGetCurrentAxisRange(int axisId, DDouble &Start, DDouble &End, bool checkMapset=FALSE);
309   void gdlGetCurrentAxisWindow(int axisId, DDouble &wStart, DDouble &wEnd);
310   void gdlStoreAxisType(int axisId, bool type);
311   void gdlGetCharSizes(GDLGStream *a, PLFLT &nsx, PLFLT &nsy, DDouble &wsx, DDouble &wsy,
312 		       DDouble &dsx, DDouble &dsy, DDouble &lsx, DDouble &lsy);
313   void GetSFromPlotStructs(DDouble **sx, DDouble **sy, DDouble **sz=NULL);
314   void GetWFromPlotStructs(DFloat **wx, DFloat **wy);
315   void setPlplotScale(GDLGStream* a);
316   void DataCoordLimits(DDouble *sx, DDouble *sy, DFloat *wx, DFloat *wy,
317 		       DDouble *xStart, DDouble *xEnd, DDouble *yStart, DDouble *yEnd, bool);
318   void stopClipping(GDLGStream *a);
319   void gdlStoreCLIP(DLongGDL* clipBox);
320   void GetCurrentUserLimits(GDLGStream *a,
321 			    DDouble &xStart, DDouble &xEnd, DDouble &yStart, DDouble &yEnd);
322   PLFLT gdlAdjustAxisRange(EnvT* e, int axisId, DDouble &val_min, DDouble &val_max, bool log = false, int calendarcode = 0);
323   PLFLT AutoTick(DDouble x);
324   void setIsoPort(GDLGStream* actStream,PLFLT x1,PLFLT x2,PLFLT y1,PLFLT y2,PLFLT aspect);
325   void GetMinMaxVal( DDoubleGDL* val, double* minVal, double* maxVal);
326   void GetMinMaxValuesForSubset( DDoubleGDL* val, DDouble &minVal, DDouble &maxVal, SizeT endElement);
327   void CheckMargin( GDLGStream* actStream,
328                     DFloat xMarginL, DFloat xMarginR, DFloat yMarginB, DFloat yMarginT,
329                     PLFLT& xMR, PLFLT& xML, PLFLT& yMB, PLFLT& yMT);
330   void UpdateSWPlotStructs(GDLGStream* actStream, DDouble xStart, DDouble xEnd, DDouble yStart,
331 			   DDouble yEnd, bool xLog, bool yLog);
332   gdlSavebox* getSaveBox();
333   gdlSavebox* getTempBox();
334   void gdlSimpleAxisTickFunc( PLINT axis, PLFLT value, char *label, PLINT length, PLPointer data);
335   void gdlSingleAxisTickNamedFunc( PLINT axis, PLFLT value, char *label, PLINT length, PLPointer data);
336   void gdlMultiAxisTickFunc(PLINT axis, PLFLT value, char *label, PLINT length, PLPointer data);
337   void doOurOwnFormat(PLINT axisNotUsed, PLFLT value, char *label, PLINT length, PLPointer data);
338 //
339 //--------------FOLLOWING ARE STATIC FUNCTIONS-----------------------------------------------
340 //This because static pointers to options indexes are needed to speed up process, but these indexes vary between
341 //the definition of the caller functions (e.g. "CHARSIZE" is 1 for CONTOUR but 7 for XYOUTS). So they need to be kept
342 //static (for speed) but private for each graphic command.
gdlSetGraphicsBackgroundColorFromKw(EnvT * e,GDLGStream * a,bool kw=true)343   static void gdlSetGraphicsBackgroundColorFromKw(EnvT *e, GDLGStream *a, bool kw=true)
344   {
345     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
346     DLong background=
347     (*static_cast<DLongGDL*>
348      (pStruct->GetTag(pStruct->Desc()->TagIndex("BACKGROUND"), 0)))[0];
349     if ( kw ) {
350       static int BACKGROUNDIx=e->KeywordIx("BACKGROUND");
351       e->AssureLongScalarKWIfPresent(BACKGROUNDIx, background);
352     }
353     DLong decomposed=GraphicsDevice::GetDevice()->GetDecomposed();
354     a->Background(background,decomposed);
355   }
gdlSetGraphicsForegroundColorFromKw(EnvT * e,GDLGStream * a,string OtherColorKw="")356   static void gdlSetGraphicsForegroundColorFromKw(EnvT *e, GDLGStream *a, string OtherColorKw="")
357   {
358     // Get COLOR from PLOT system variable
359     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
360     DLong color=
361     (*static_cast<DLongGDL*>
362      (pStruct->GetTag(pStruct->Desc()->TagIndex("COLOR"), 0)))[0];
363 
364     DLongGDL *colorVect;
365     static int colorIx=e->KeywordIx ( "COLOR" );
366     int realcolorIx=colorIx;
367     //eventually do not get color from standard "COLOR" keyword but from another...
368     if (OtherColorKw != "") realcolorIx=e->KeywordIx (OtherColorKw);
369     if ( e->GetKW ( realcolorIx )!=NULL )
370     {
371       colorVect=e->GetKWAs<DLongGDL>( realcolorIx ); //color can be vectorial, but...
372       color=(*colorVect)[0]; //this function only sets color to 1st arg in list!
373     }
374     // Get decomposed value for colors
375     DLong decomposed=GraphicsDevice::GetDevice()->GetDecomposed();
376     a->Color(color, decomposed);
377   }
378 
gdlGetPsym(EnvT * e,DLong & psym)379   static void gdlGetPsym(EnvT *e, DLong &psym)
380   {
381     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
382     psym=(*static_cast<DLongGDL*>
383           (pStruct->GetTag(pStruct->Desc()->TagIndex("PSYM"), 0)))[0];
384     static int PSYMIx=e->KeywordIx("PSYM");
385     e->AssureLongScalarKWIfPresent(PSYMIx, psym);
386     if ( psym>10||psym < -8||psym==9 )
387       e->Throw(
388                "PSYM (plotting symbol) out of range.");
389   }
gdlSetSymsize(EnvT * e,GDLGStream * a)390    static void gdlSetSymsize(EnvT *e, GDLGStream *a)
391   {
392     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
393     DFloat symsize=(*static_cast<DFloatGDL*>
394                     (pStruct->GetTag(pStruct->Desc()->TagIndex("SYMSIZE"), 0)))[0];
395                     //NOTE THAT AS OF IDL 8.2 !P.SYMSIZE, HOWEVER EXISTING, IS NOT TAKEN INTO ACCOUNT. We however do not want
396                     //to reproduce this feature.
397     static int SYMSIZEIx=e->KeywordIx("SYMSIZE");
398     e->AssureFloatScalarKWIfPresent(SYMSIZEIx, symsize);
399     if ( symsize<=0.0 ) symsize=1.0;
400     a->setSymbolSize(symsize);
401   }
402 //  static void GetUserSymSize(EnvT *e, GDLGStream *a, DDouble& UsymConvX, DDouble& UsymConvY)
403 //  {
404 //    //get symsize
405 //    DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
406 //    DFloat symsize=(*static_cast<DFloatGDL*>
407 //                    (pStruct->GetTag(pStruct->Desc()->TagIndex("SYMSIZE"), 0)))[0];
408 //    static int SYMSIZEIx = e->KeywordIx("SYMSIZE");
409 //    e->AssureFloatScalarKWIfPresent(SYMSIZEIx, symsize);
410 //    if ( symsize<=0.0 ) symsize=1.0;
411 //
412 //    UsymConvX=(0.5*symsize*(a->wCharLength()/a->charScale())); //be dependent only on symsize!
413 //    UsymConvY=(0.5*symsize*(a->wCharHeight()/a->charScale()));
414 //    PLFLT wun, wdeux, wtrois, wquatre; //take care of axes world orientation!
415 //    a->pageWorldCoordinates(wun, wdeux, wtrois, wquatre);
416 //    if ((wdeux-wun)<0) UsymConvX*=-1.0;
417 //    if ((wquatre-wtrois)<0) UsymConvY*=-1.0;
418 //    if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"GetUserSymSize(%f,%f), charlen=%f, charheight=%f, charscale=%f\n",
419 //				    UsymConvX, UsymConvY,a->wCharLength(),a->wCharHeight(),a->charScale());
420 //  }
gdlSetPlotCharsize(EnvT * e,GDLGStream * a,bool accept_sizeKw=false)421   static void gdlSetPlotCharsize(EnvT *e, GDLGStream *a, bool accept_sizeKw=false)
422   {
423     PLFLT charsize;
424     DDouble pmultiscale=1.0;
425     // get !P preference or !FANCY ... they should agree as charsize = 0.2*FANCY+0.8
426     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
427     DFloat* charsizePos=&((*static_cast<DFloatGDL*>(pStruct->GetTag(pStruct->Desc()->TagIndex("CHARSIZE"), 0)))[0]);
428     charsize=charsizePos[0];
429 //    //if charsize==0 see if !FANCY is set to something above 1 or below 1
430 //    DIntGDL* fancy= SysVar::GetFancy();
431 //    if ((*fancy)[0] > -4) { //negative values are a mess
432 //     PLFLT fancySize = 0.2 * (*fancy)[0] + 0.8;
433 //     if (fancySize != charsize) { //make them agree
434 //      charsize = fancySize;
435 //      charsizePos[0] = charsize;
436 //     }
437 //    }
438     //overload with command preference. Charsize may be a vector now in some gdl commands, take care of it:
439     if (accept_sizeKw) //XYOUTS specials!
440     {
441       static int SIZEIx=e->KeywordIx("SIZE"); //define here only (else trig an assert() )
442       DFloat fcharsize;
443       fcharsize=charsize;
444       e->AssureFloatScalarKWIfPresent(SIZEIx, fcharsize);
445       charsize=fcharsize;
446     }
447     static int charsizeIx=e->KeywordIx ( "CHARSIZE" );
448     if ( e->GetKW ( charsizeIx )!=NULL )
449     {
450       DFloatGDL* charsizeVect=e->GetKWAs<DFloatGDL>( charsizeIx );
451       charsize=(*charsizeVect)[0];
452     }
453     if ( charsize<=0.0 ) charsize=1.0;
454     // adjust if MULTI:
455     DLongGDL* pMulti=SysVar::GetPMulti();
456     if ( (*pMulti)[1]>2||(*pMulti)[2]>2 ) pmultiscale=0.5;
457     a->sizeChar(charsize*pmultiscale);
458   }
459 
gdlSetPlotCharthick(EnvT * e,GDLGStream * a)460    static void gdlSetPlotCharthick(EnvT *e, GDLGStream *a)
461   {
462      // get !P preference
463     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
464     DFloat charthick=(*static_cast<DFloatGDL*>
465               (pStruct->GetTag
466                (pStruct->Desc()->TagIndex("CHARTHICK"), 0)))[0];
467     static int charthickIx=e->KeywordIx ( "CHARTHICK" ); //Charthick values may be vector in GDL, not in IDL!
468     if ( e->GetKW ( charthickIx )!=NULL )
469     {
470       DFloatGDL* charthickVect=e->GetKWAs<DFloatGDL>( charthickIx );
471       charthick=(*charthickVect)[0];
472     }
473     if ( charthick <= 0.0 ) charthick=1.0;
474     a->Thick(charthick);
475   }
476 
gdlComputeTickInterval(EnvT * e,int axisId,DDouble & min,DDouble & max,bool log)477   static PLFLT gdlComputeTickInterval(EnvT *e, int axisId, DDouble &min, DDouble &max, bool log)
478   {
479     DLong nticks=0;
480 
481     static int XTICKSIx = e->KeywordIx("XTICKS");
482     static int YTICKSIx = e->KeywordIx("YTICKS");
483     static int ZTICKSIx = e->KeywordIx("ZTICKS");
484     int choosenIx=XTICKSIx;
485     DStructGDL* Struct=NULL;
486     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKSIx; }
487     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKSIx; }
488     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKSIx; }
489 
490     if ( Struct!=NULL )
491     {
492       unsigned tickTag=Struct->Desc()->TagIndex("TICKS");
493       nticks=(*static_cast<DLongGDL*>(Struct->GetTag(tickTag, 0)))[0];
494     }
495     e->AssureLongScalarKWIfPresent(choosenIx, nticks);
496 
497     PLFLT intv;
498     if (nticks == 0)
499     {
500       intv = (log)? AutoTick(log10(max-min)): AutoTick(max-min);
501     } else {
502       intv = (log)? log10(max-min)/nticks: (max-min)/nticks;
503     }
504     return intv;
505   }
506 
gdlGetDesiredAxisCharsize(EnvT * e,int axisId,DFloat & charsize)507   static void gdlGetDesiredAxisCharsize(EnvT* e, int axisId, DFloat &charsize)
508   {
509     //default:
510     charsize=1.0;
511     // get !P preference. Even if [xyz]charsize is absent, presence of charsize or !P.charsize must be taken into account.
512     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
513     charsize=(*static_cast<DFloatGDL*>
514               (pStruct->GetTag
515               (pStruct->Desc()->TagIndex("CHARSIZE"), 0)))[0];
516     static int CharsizeIx= e->KeywordIx( "CHARSIZE");
517     //cerr<<" CHARSIZE: "<< CharsizeIx<<" ("<< &CharsizeIx<<")"<<endl;
518     e->AssureFloatScalarKWIfPresent(CharsizeIx, charsize); // option charsize overloads P.CHARSIZE
519     if (charsize==0) charsize=1.0;
520     // Axis Preference. Is a Multiplier!
521     static int XCharsizeIx = e->KeywordIx("XCHARSIZE");
522     static int YCharsizeIx = e->KeywordIx("YCHARSIZE");
523     static int ZCharsizeIx = e->KeywordIx("ZCHARSIZE");
524     int choosenIx=XCharsizeIx;
525     DStructGDL* Struct=NULL;
526     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XCharsizeIx; }
527     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YCharsizeIx; }
528     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZCharsizeIx; }
529 
530     if ( Struct!=NULL )
531     {
532       unsigned charsizeTag=Struct->Desc()->TagIndex("CHARSIZE"); //[XYZ].CHARSIZE
533       DFloat axisCharsizeMultiplier=(*static_cast<DFloatGDL*>(Struct->GetTag(charsizeTag, 0)))[0];
534       e->AssureFloatScalarKWIfPresent(choosenIx, axisCharsizeMultiplier); //option [XYZ]CHARSIZE overloads ![XYZ].CHARSIZE
535       if (axisCharsizeMultiplier>0.0) charsize*=axisCharsizeMultiplier; //IDL Behaviour...
536     }
537   }
gdlSetAxisCharsize(EnvT * e,GDLGStream * a,int axisId)538   static  void gdlSetAxisCharsize(EnvT *e, GDLGStream *a, int axisId)
539   {
540 
541     DFloat charsize=0.0;
542     DDouble pmultiscale=1.0;
543     gdlGetDesiredAxisCharsize(e, axisId, charsize);
544     // adjust if MULTI:
545     DLongGDL* pMulti=SysVar::GetPMulti();
546     if ( (*pMulti)[1]>2||(*pMulti)[2]>2 ) pmultiscale=0.5; //IDL behaviour
547     // scale default value (which depends on number of subpages)
548     // a->schr(0.0, charsize*pmultiscale);
549     a->sizeChar(charsize*pmultiscale);
550   }
551 
gdlGetDesiredAxisGridStyle(EnvT * e,int axisId,DLong & axisGridstyle)552   static void gdlGetDesiredAxisGridStyle(EnvT* e, int axisId, DLong &axisGridstyle)
553   {
554     axisGridstyle=0;
555     DStructGDL* Struct=NULL;
556     static int XGRIDSTYLEIx = e->KeywordIx("XGRIDSTYLE");
557     static int YGRIDSTYLEIx = e->KeywordIx("YGRIDSTYLE");
558     static int ZGRIDSTYLEIx = e->KeywordIx("ZGRIDSTYLE");
559     int choosenIx=XGRIDSTYLEIx;
560     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XGRIDSTYLEIx; }
561     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YGRIDSTYLEIx; }
562     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZGRIDSTYLEIx; }
563 
564     if ( Struct!=NULL )
565     {
566       unsigned gridstyleTag=Struct->Desc()->TagIndex("GRIDSTYLE");
567       axisGridstyle=(*static_cast<DLongGDL*>(Struct->GetTag(gridstyleTag, 0)))[0];
568       e->AssureLongScalarKWIfPresent(choosenIx, axisGridstyle);
569     }
570   }
gdlGetDesiredAxisMargin(EnvT * e,int axisId,DFloat & start,DFloat & end)571   static void gdlGetDesiredAxisMargin(EnvT *e, int axisId, DFloat &start, DFloat &end)
572   {
573     static int XMARGINIx = e->KeywordIx("XMARGIN");
574     static int YMARGINIx = e->KeywordIx("YMARGIN");
575     static int ZMARGINIx = e->KeywordIx("ZMARGIN");
576     int choosenIx=XMARGINIx;
577     DStructGDL* Struct=NULL;
578     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XMARGINIx; }
579     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YMARGINIx; }
580     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZMARGINIx; }
581 
582     if ( Struct!=NULL )
583     {
584       unsigned marginTag=Struct->Desc()->TagIndex("MARGIN");
585       start= (*static_cast<DFloatGDL*>(Struct->GetTag(marginTag, 0)))[0];
586       end  = (*static_cast<DFloatGDL*>(Struct->GetTag(marginTag, 0)))[1];
587     }
588 
589     BaseGDL* Margin=e->GetKW(choosenIx);
590     if ( Margin!=NULL )
591     {
592       if ( Margin->N_Elements()>2 )
593         e->Throw("Keyword array parameter "+axisName[axisId]+"MARGIN must have from 1 to 2 elements.");
594       Guard<DFloatGDL> guard;
595       DFloatGDL* MarginF=static_cast<DFloatGDL*>
596       (Margin->Convert2(GDL_FLOAT, BaseGDL::COPY));
597       guard.Reset(MarginF);
598       start=(*MarginF)[0];
599       if ( MarginF->N_Elements()>1 )
600         end=(*MarginF)[1];
601     }
602   }
gdlGetDesiredAxisMinor(EnvT * e,int axisId,DLong & axisMinor)603   static void gdlGetDesiredAxisMinor(EnvT* e, int axisId, DLong &axisMinor)
604   {
605     axisMinor=0;
606     static int XMINORIx = e->KeywordIx("XMINOR");
607     static int YMINORIx = e->KeywordIx("YMINOR");
608     static int ZMINORIx = e->KeywordIx("ZMINOR");
609     int choosenIx=XMINORIx;
610     DStructGDL* Struct=NULL;
611     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XMINORIx; }
612     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YMINORIx; }
613     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZMINORIx; }
614    if ( Struct!=NULL )
615     {
616       unsigned AxisMinorTag=Struct->Desc()->TagIndex("MINOR");
617       axisMinor=(*static_cast<DLongGDL*>(Struct->GetTag(AxisMinorTag,0)))[0];
618     }
619     e->AssureLongScalarKWIfPresent(choosenIx, axisMinor);
620   }
gdlGetDesiredAxisRange(EnvT * e,int axisId,DDouble & start,DDouble & end)621   static bool gdlGetDesiredAxisRange(EnvT *e, int axisId, DDouble &start, DDouble &end)
622   {
623     bool set=FALSE;
624     static int XRANGEIx = e->KeywordIx("XRANGE");
625     static int YRANGEIx = e->KeywordIx("YRANGE");
626     static int ZRANGEIx = e->KeywordIx("ZRANGE");
627     int choosenIx=XRANGEIx;
628     DStructGDL* Struct=NULL;
629     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XRANGEIx; }
630     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YRANGEIx; }
631     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZRANGEIx; }
632     if ( Struct!=NULL )
633     {
634       DDouble test1, test2;
635       unsigned rangeTag=Struct->Desc()->TagIndex("RANGE");
636       test1=(*static_cast<DDoubleGDL*>(Struct->GetTag(rangeTag, 0)))[0];
637       test2=(*static_cast<DDoubleGDL*>(Struct->GetTag(rangeTag, 0)))[1];
638       if ( !((test1-test2)==0.0) )
639       {
640         start=test1;
641         end=test2;
642         set=true;
643       }
644     }
645     BaseGDL* Range=e->GetKW(choosenIx);
646     if ( Range!=NULL )
647     {
648       if ( Range->N_Elements()!=2 )
649         e->Throw("Keyword array parameter "+axisName[axisId]+"RANGE must have 2 elements.");
650       Guard<DDoubleGDL> guard;
651       DDoubleGDL* RangeF=static_cast<DDoubleGDL*>(Range->Convert2(GDL_DOUBLE, BaseGDL::COPY));
652       guard.Reset(RangeF);
653       if (!(((*RangeF)[0]-(*RangeF)[1])==0.0))
654       {
655         start=(*RangeF)[0];
656         end=(*RangeF)[1];
657         set=true;
658       }
659     }
660     return set;
661   }
gdlGetDesiredAxisStyle(EnvT * e,int axisId,DLong & style)662   static  void gdlGetDesiredAxisStyle(EnvT *e, int axisId, DLong &style)
663   {
664     static int XSTYLEIx = e->KeywordIx("XSTYLE");
665     static int YSTYLEIx = e->KeywordIx("YSTYLE");
666     static int ZSTYLEIx = e->KeywordIx("ZSTYLE");
667     int choosenIx=XSTYLEIx;
668     DStructGDL* Struct=NULL;
669     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XSTYLEIx; }
670     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YSTYLEIx; }
671     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZSTYLEIx; }
672 
673     if ( Struct!=NULL )
674     {
675       int styleTag=Struct->Desc()->TagIndex("STYLE");
676       style= (*static_cast<DLongGDL*>(Struct->GetTag(styleTag, 0)))[0];
677     }
678 
679     e->AssureLongScalarKWIfPresent( choosenIx, style);
680   }
gdlGetDesiredAxisThick(EnvT * e,int axisId,DFloat & thick)681     static void gdlGetDesiredAxisThick(EnvT *e,  int axisId, DFloat &thick)
682   {
683     thick=1.0;
684     static int XTHICKIx = e->KeywordIx("XTHICK");
685     static int YTHICKIx = e->KeywordIx("YTHICK");
686     static int ZTHICKIx = e->KeywordIx("ZTHICK");
687     int choosenIx=XTHICKIx;
688     DStructGDL* Struct=NULL;
689     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTHICKIx; }
690     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTHICKIx; }
691     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTHICKIx; }
692 
693     if ( Struct!=NULL )
694     {
695       //not static!
696       int thickTag=Struct->Desc()->TagIndex("THICK");
697       thick = (*static_cast<DFloatGDL*>(Struct->GetTag(thickTag, 0)))[0];
698     }
699     e->AssureFloatScalarKWIfPresent(choosenIx, thick);
700     if ( thick <= 0.0 ) thick=1.0;
701   }
gdlGetDesiredAxisTickget(EnvT * e,int axisId,DDoubleGDL * Axistickget)702    static void gdlGetDesiredAxisTickget(EnvT *e,  int axisId, DDoubleGDL *Axistickget)
703   {
704     //TODO!
705   }
706 
gdlGetDesiredAxisTickFormat(EnvT * e,int axisId,DStringGDL * & axisTickformatVect)707   static void gdlGetDesiredAxisTickFormat(EnvT* e, int axisId, DStringGDL* &axisTickformatVect)
708   {
709     static int XTICKFORMATIx = e->KeywordIx("XTICKFORMAT");
710     static int YTICKFORMATIx = e->KeywordIx("YTICKFORMAT");
711     static int ZTICKFORMATIx = e->KeywordIx("ZTICKFORMAT");
712     int choosenIx=XTICKFORMATIx;
713     DStructGDL* Struct=NULL;
714     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKFORMATIx; }
715     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKFORMATIx; }
716     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKFORMATIx; }
717 
718    if ( Struct!=NULL )
719     {
720       unsigned AxisTickformatTag=Struct->Desc()->TagIndex("TICKFORMAT");
721       axisTickformatVect = static_cast<DStringGDL*>(Struct->GetTag(AxisTickformatTag,0));
722     }
723     if ( e->GetKW ( choosenIx )!=NULL )
724     {
725       axisTickformatVect=e->GetKWAs<DStringGDL>( choosenIx );
726     }
727   }
728 
gdlGetDesiredAxisTickInterval(EnvT * e,int axisId,DDouble & axisTickinterval)729   static void gdlGetDesiredAxisTickInterval(EnvT* e, int axisId, DDouble &axisTickinterval)
730   {
731     axisTickinterval=0;
732     static int XTICKINTERVALIx = e->KeywordIx("XTICKINTERVAL");
733     static int YTICKINTERVALIx = e->KeywordIx("YTICKINTERVAL");
734     static int ZTICKINTERVALIx = e->KeywordIx("ZTICKINTERVAL");
735     int choosenIx=XTICKINTERVALIx;
736     DStructGDL* Struct=NULL;
737     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKINTERVALIx; }
738     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKINTERVALIx; }
739     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKINTERVALIx; }
740 
741     if ( Struct!=NULL )
742     {
743       axisTickinterval=(*static_cast<DDoubleGDL*>
744                 (Struct->GetTag
745                 (Struct->Desc()->TagIndex("TICKINTERVAL"), 0)))[0];
746     }
747     e->AssureDoubleScalarKWIfPresent(choosenIx, axisTickinterval);
748   }
749 
gdlGetDesiredAxisTickLayout(EnvT * e,int axisId,DLong & axisTicklayout)750   static void gdlGetDesiredAxisTickLayout(EnvT* e, int axisId, DLong &axisTicklayout)
751   {
752     axisTicklayout=0;
753     static int XTICKLAYOUTIx = e->KeywordIx("XTICKLAYOUT");
754     static int YTICKLAYOUTIx = e->KeywordIx("YTICKLAYOUT");
755     static int ZTICKLAYOUTIx = e->KeywordIx("ZTICKLAYOUT");
756     int choosenIx=XTICKLAYOUTIx;
757     DStructGDL* Struct=NULL;
758     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKLAYOUTIx; }
759     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKLAYOUTIx; }
760     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKLAYOUTIx; }
761     if ( Struct!=NULL )
762     {
763       axisTicklayout=(*static_cast<DLongGDL*>
764                 (Struct->GetTag
765                 (Struct->Desc()->TagIndex("TICKLAYOUT"), 0)))[0];
766     }
767     e->AssureLongScalarKWIfPresent(choosenIx, axisTicklayout);
768   }
769 
gdlGetDesiredAxisTickLen(EnvT * e,int axisId,DFloat & ticklen)770   static void gdlGetDesiredAxisTickLen(EnvT* e, int axisId, DFloat &ticklen)
771   {
772     // order: !P.TICKLEN, TICKLEN, !X.TICKLEN, /XTICKLEN
773     // get !P preference
774     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
775     ticklen=(*static_cast<DFloatGDL*>
776             (pStruct->GetTag
777             (pStruct->Desc()->TagIndex("TICKLEN"), 0)))[0]; //!P.TICKLEN, always exist, may be 0
778     static int TICKLENIx = e->KeywordIx("TICKLEN");
779     e->AssureFloatScalarKWIfPresent(TICKLENIx, ticklen); //overwritten by TICKLEN option
780 
781     static int XTICKLENIx = e->KeywordIx("XTICKLEN");
782     static int YTICKLENIx = e->KeywordIx("YTICKLEN");
783     static int ZTICKLENIx = e->KeywordIx("ZTICKLEN");
784     int choosenIx=XTICKLENIx;
785     DStructGDL* Struct=NULL;
786     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKLENIx; }
787     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKLENIx; }
788     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKLENIx; }
789     if ( Struct!=NULL )
790     {
791       unsigned ticklenTag=Struct->Desc()->TagIndex("TICKLEN");
792       DFloat axisTicklen=(*static_cast<DFloatGDL*>(Struct->GetTag(ticklenTag, 0)))[0]; //![XYZ].TICKLEN (exist)
793       e->AssureFloatScalarKWIfPresent(choosenIx, axisTicklen); //overriden by kw
794       if (axisTicklen!=0.0) ticklen=axisTicklen;
795     }
796   }
797 
gdlGetDesiredAxisTickName(EnvT * e,GDLGStream * a,int axisId,DStringGDL * & axisTicknameVect)798  static void gdlGetDesiredAxisTickName(EnvT* e, GDLGStream* a, int axisId, DStringGDL* &axisTicknameVect)
799   {
800 
801     static int XTICKNAMEIx = e->KeywordIx("XTICKNAME");
802     static int YTICKNAMEIx = e->KeywordIx("YTICKNAME");
803     static int ZTICKNAMEIx = e->KeywordIx("ZTICKNAME");
804     int choosenIx=XTICKNAMEIx;
805     DStructGDL* Struct=NULL;
806     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKNAMEIx; }
807     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKNAMEIx; }
808     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKNAMEIx; }
809     if ( Struct!=NULL )
810     {
811       unsigned AxisTicknameTag=Struct->Desc()->TagIndex("TICKNAME");
812       axisTicknameVect=static_cast<DStringGDL*>(Struct->GetTag(AxisTicknameTag,0));
813     }
814     if ( e->GetKW ( choosenIx )!=NULL )
815     {
816       axisTicknameVect=e->GetKWAs<DStringGDL>( choosenIx );
817       //translate format codes here:
818 //      for (SizeT iname=0; iname < axisTicknameVect->N_Elements(); ++iname) {
819 //        std::string out = std::string("");
820 //        a->TranslateFormatCodes(((*axisTicknameVect)[iname]).c_str(),out);
821 ////TBD: not finished, see cases not treated in TransmateFormatCodes (gdlgstream.cpp)
822 //        (*axisTicknameVect)[iname]=out;
823 //      }
824     }
825 
826   }
827 
gdlGetDesiredAxisTicks(EnvT * e,int axisId,DLong & axisTicks)828   static void gdlGetDesiredAxisTicks(EnvT* e, int axisId, DLong &axisTicks)
829   {
830     axisTicks=0;
831 
832     static int XTICKSIx = e->KeywordIx("XTICKS");
833     static int YTICKSIx = e->KeywordIx("YTICKS");
834     static int ZTICKSIx = e->KeywordIx("ZTICKS");
835     int choosenIx=XTICKSIx;
836     DStructGDL* Struct=NULL;
837     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKSIx; }
838     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKSIx; }
839     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKSIx; }
840 
841     if ( Struct!=NULL )
842     {
843       axisTicks=(*static_cast<DLongGDL*>
844                 (Struct->GetTag
845                 (Struct->Desc()->TagIndex("TICKS"), 0)))[0];
846     }
847     e->AssureLongScalarKWIfPresent(choosenIx, axisTicks);
848     if (axisTicks > 59) e->Throw("Value of number of ticks is out of allowed range.");
849   }
850 
851 
852   //if axis tick units is specified, first tickunit determines how the automatic limits are computed.
853   // for example, if tickunits=['year','day'] the limits will be on a round nuber of years.
854   // This is conveyed by the code
gdlGetCalendarCode(EnvT * e,int axisId)855   static int gdlGetCalendarCode(EnvT* e, int axisId)
856   {
857     static int XTICKUNITSIx = e->KeywordIx("XTICKUNITS");
858     static int YTICKUNITSIx = e->KeywordIx("YTICKUNITS");
859     static int ZTICKUNITSIx = e->KeywordIx("ZTICKUNITS");
860     int choosenIx=XTICKUNITSIx;
861     DStructGDL* Struct=NULL;
862     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKUNITSIx; }
863     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKUNITSIx; }
864     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKUNITSIx; }
865     DStringGDL* axisTickunitsVect=NULL;
866     if ( Struct!=NULL )
867     {
868       unsigned AxisTickunitsTag=Struct->Desc()->TagIndex("TICKUNITS");
869       axisTickunitsVect=static_cast<DStringGDL*>(Struct->GetTag(AxisTickunitsTag,0));
870     }
871     if ( e->GetKW ( choosenIx )!=NULL )
872     {
873       axisTickunitsVect=e->GetKWAs<DStringGDL>( choosenIx );
874     }
875     int code=0;
876     DString what=StrUpCase((*axisTickunitsVect)[0]);
877     if (what.substr(0,4)=="YEAR") code=1;
878     else if (what.substr(0,5)=="MONTH") code=2;
879     else if (what.substr(0,3)=="DAY") code=3;
880     else if (what.substr(0,7)=="NUMERIC") code=3;
881     else if (what.substr(0,4)=="HOUR") code=4;
882     else if (what.substr(0,6)=="MINUTE") code=5;
883     else if (what.substr(0,6)=="SECOND") code=6;
884     else if (what.substr(0,4)=="TIME") code=7;
885     return code;
886   }
887 
gdlGetDesiredAxisTickUnits(EnvT * e,int axisId,DStringGDL * & axisTickunitsVect)888  static void gdlGetDesiredAxisTickUnits(EnvT* e, int axisId, DStringGDL* &axisTickunitsVect)
889   {
890     static int XTICKUNITSIx = e->KeywordIx("XTICKUNITS");
891     static int YTICKUNITSIx = e->KeywordIx("YTICKUNITS");
892     static int ZTICKUNITSIx = e->KeywordIx("ZTICKUNITS");
893     int choosenIx=XTICKUNITSIx;
894     DStructGDL* Struct=NULL;
895     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKUNITSIx; }
896     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKUNITSIx; }
897     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKUNITSIx; }
898    if ( Struct!=NULL )
899     {
900       unsigned AxisTickunitsTag=Struct->Desc()->TagIndex("TICKUNITS");
901       axisTickunitsVect=static_cast<DStringGDL*>(Struct->GetTag(AxisTickunitsTag,0));
902     }
903     if ( e->GetKW ( choosenIx )!=NULL )
904     {
905       axisTickunitsVect=e->GetKWAs<DStringGDL>( choosenIx );
906     }
907       }
908 
gdlGetDesiredAxisTickv(EnvT * e,int axisId,DDoubleGDL * axisTickvVect)909   static void gdlGetDesiredAxisTickv(EnvT* e, int axisId, DDoubleGDL* axisTickvVect)
910   {
911     static int XTICKVIx = e->KeywordIx("XTICKV");
912     static int YTICKVIx = e->KeywordIx("YTICKV");
913     static int ZTICKVIx = e->KeywordIx("ZTICKV");
914     int choosenIx=XTICKVIx;
915     DStructGDL* Struct=NULL;
916     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTICKVIx; }
917     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTICKVIx; }
918     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTICKVIx; }
919     if ( Struct!=NULL )
920     {
921       unsigned AxisTickvTag=Struct->Desc()->TagIndex("TICKV");
922       axisTickvVect=static_cast<DDoubleGDL*>(Struct->GetTag(AxisTickvTag,0));
923 
924     }
925     if ( e->GetKW ( choosenIx )!=NULL )
926     {
927       axisTickvVect=e->GetKWAs<DDoubleGDL>( choosenIx );
928     }
929   }
930 
gdlGetDesiredAxisTitle(EnvT * e,int axisId,DString & title)931   static void gdlGetDesiredAxisTitle(EnvT *e, int axisId, DString &title)
932   {
933     static int XTITLEIx = e->KeywordIx("XTITLE");
934     static int YTITLEIx = e->KeywordIx("YTITLE");
935     static int ZTITLEIx = e->KeywordIx("ZTITLE");
936     int choosenIx=XTITLEIx;
937     DStructGDL* Struct=NULL;
938     if ( axisId==XAXIS ) { Struct=SysVar::X(); choosenIx=XTITLEIx; }
939     if ( axisId==YAXIS ) { Struct=SysVar::Y(); choosenIx=YTITLEIx; }
940     if ( axisId==ZAXIS ) { Struct=SysVar::Z(); choosenIx=ZTITLEIx; }
941 
942     if ( Struct!=NULL )
943     {
944       unsigned titleTag=Struct->Desc()->TagIndex("TITLE");
945       title=
946       (*static_cast<DStringGDL*>(Struct->GetTag(titleTag, 0)))[0];
947     }
948 
949     e->AssureStringScalarKWIfPresent(choosenIx, title);
950   }
951 
gdlSetLineStyle(EnvT * e,GDLGStream * a)952     static void gdlSetLineStyle(EnvT *e, GDLGStream *a)
953   {
954     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
955     DLong linestyle=
956     (*static_cast<DLongGDL*>
957      (pStruct->GetTag(pStruct->Desc()->TagIndex("LINESTYLE"), 0)))[0];
958 
959     // if the LINESTYLE keyword is present, the value will be change
960     DLong linestyleNew=-1111;
961     static int linestyleIx = e->KeywordIx("LINESTYLE");
962 
963     if (e->KeywordSet(linestyleIx)) e->AssureLongScalarKWIfPresent(linestyleIx, linestyleNew);
964 
965     bool debug=false;
966     if ( debug )
967     {
968       cout<<"temp_linestyle "<<linestyleNew<<endl;
969       cout<<"     linestyle "<<linestyle<<endl;
970     }
971     if ( linestyleNew!= -1111 )
972     {
973       linestyle=linestyleNew;
974     }//+1;
975     if ( linestyle<0 )
976     {
977       linestyle=0;
978     }
979     if ( linestyle>5 )
980     {
981       linestyle=5;
982     }
983     gdlLineStyle(a, linestyle);
984   }
985 
986 
987 
gdlGetPenThickness(EnvT * e,GDLGStream * a)988   static DFloat gdlGetPenThickness(EnvT *e, GDLGStream *a)
989   {
990     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
991     DFloat thick=(*static_cast<DFloatGDL*>
992                   (pStruct->GetTag(pStruct->Desc()->TagIndex("THICK"), 0)))[0];
993 
994     static int THICKIx = e->KeywordIx("THICK");
995     e->AssureFloatScalarKWIfPresent(THICKIx, thick);
996     if ( thick <= 0.0 ) thick=1.0;
997     return thick;
998   }
999 
gdlSetPenThickness(EnvT * e,GDLGStream * a)1000   static void gdlSetPenThickness(EnvT *e, GDLGStream *a)
1001   {
1002     a->Thick(gdlGetPenThickness(e, a));
1003   }
1004 
gdlWriteTitleAndSubtitle(EnvT * e,GDLGStream * a)1005   static void gdlWriteTitleAndSubtitle(EnvT* e, GDLGStream *a)
1006   {
1007     unsigned titleTag=SysVar::P()->Desc()->TagIndex("TITLE");
1008     unsigned subTitleTag=SysVar::P()->Desc()->TagIndex("SUBTITLE");
1009     DString title=(*static_cast<DStringGDL*>(SysVar::P()->GetTag(titleTag, 0)))[0];
1010     DString subTitle=(*static_cast<DStringGDL*>(SysVar::P()->GetTag(subTitleTag, 0)))[0];
1011 
1012     static int TITLEIx = e->KeywordIx("TITLE");
1013     static int SUBTITLEIx = e->KeywordIx("SUBTITLE");
1014     e->AssureStringScalarKWIfPresent(TITLEIx, title);
1015     e->AssureStringScalarKWIfPresent(SUBTITLEIx, subTitle);
1016     if (title.empty() && subTitle.empty()) return;
1017 
1018     gdlSetPlotCharsize(e, a);
1019     if (!title.empty())
1020     {
1021       e->AssureStringScalarKWIfPresent(TITLEIx, title);
1022       gdlSetPlotCharthick(e, a);
1023       a->sizeChar(1.25*a->charScale());
1024       a->mtex("t", 1.5, 0.5, 0.5, title.c_str()); //position is in units of current char height. baseline at half-height
1025       a->sizeChar(a->charScale()/1.25);
1026     }
1027     if (!subTitle.empty())
1028     {
1029       e->AssureStringScalarKWIfPresent(SUBTITLEIx, subTitle);
1030       DFloat step=a->mmLineSpacing()/a->mmCharHeight();
1031       a->mtex("b", 5*step, 0.5, 0.5, subTitle.c_str());
1032     }
1033  }
1034   //call this function if Y data is strictly >0.
1035   //set yStart to 0 only if gdlYaxisNoZero is false.
gdlYaxisNoZero(EnvT * e)1036     static bool gdlYaxisNoZero(EnvT* e)
1037   {
1038     //no explict range given?
1039     DDouble test1, test2;
1040     unsigned rangeTag=SysVar::Y()->Desc()->TagIndex("RANGE");
1041     test1=(*static_cast<DDoubleGDL*>(SysVar::Y()->GetTag(rangeTag, 0)))[0];
1042     test2=(*static_cast<DDoubleGDL*>(SysVar::Y()->GetTag(rangeTag, 0)))[1];
1043     if(!(test1==0.0 && test2==0.0)) return TRUE;
1044     static int YRANGEIx=e->KeywordIx( "YRANGE");
1045 
1046     if ( e->KeywordPresent( YRANGEIx)) return TRUE;
1047     //Style contains 1?
1048     DLong ystyle;
1049     gdlGetDesiredAxisStyle(e, YAXIS, ystyle);
1050     if (ystyle&1) return TRUE;
1051 
1052     DLong nozero=0;
1053     if (ystyle&16) nozero=1;
1054     static int YNOZEROIx=e->KeywordIx( "YNOZERO");
1055     if ( e->KeywordSet(YNOZEROIx)) nozero = 1;
1056     return (nozero==1);
1057   }
1058 
1059 
1060   //advance to next plot unless the noerase flag is set
1061   // function declared static (local to each function using it) to avoid messing the NOERASEIx index which is not the same.
gdlNextPlotHandlingNoEraseOption(EnvT * e,GDLGStream * a,bool noe=0)1062   static void gdlNextPlotHandlingNoEraseOption(EnvT *e, GDLGStream *a, bool noe=0)
1063   {
1064     bool noErase=FALSE;
1065     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
1066 
1067     if ( !noe )
1068     {
1069       DLong LnoErase=(*static_cast<DLongGDL*>
1070                       (pStruct->
1071                        GetTag(pStruct->Desc()->TagIndex("NOERASE"), 0)))[0];
1072       noErase=(LnoErase==1);
1073       static int NOERASEIx = e->KeywordIx("NOERASE");
1074 
1075       if ( e->KeywordSet(NOERASEIx) )
1076       {
1077         noErase=TRUE;
1078       }
1079     }
1080     else
1081     {
1082       noErase=TRUE;
1083     }
1084 
1085     a->NextPlot(!noErase);
1086       // all but the first element of !P.MULTI are ignored if POSITION kw or !P.POSITION or !P.REGION is specified
1087     // TODO: !P.REGION!
1088 
1089     DFloatGDL* pos=NULL;
1090 
1091     // system variable !P.REGION first ?? TODO
1092     pos=static_cast<DFloatGDL*>(pStruct-> GetTag(pStruct->Desc()->TagIndex("POSITION"), 0));
1093     if ( (*pos)[0]==(*pos)[2] ) pos=NULL; //ignored
1094 
1095     // keyword
1096     if ( pos==NULL )
1097     {
1098       static int positionIx=e->KeywordIx("POSITION");
1099       if ( e->GetKW ( positionIx )!=NULL )
1100       {
1101        pos=e->GetKWAs<DFloatGDL>(positionIx);
1102       }
1103     }
1104     if ( pos!=NULL ) a->NoSub();
1105   }
gdlSet3DViewPortAndWorldCoordinates(EnvT * e,GDLGStream * actStream,DDoubleGDL * Matrix,bool xLog,bool yLog,DDouble xStart,DDouble xEnd,DDouble yStart,DDouble yEnd,DDouble zStart=0.0,DDouble zEnd=1.0,bool zLog=false)1106   static bool gdlSet3DViewPortAndWorldCoordinates(EnvT* e,
1107                                            GDLGStream* actStream,
1108                                            DDoubleGDL* Matrix,
1109                                            bool xLog, bool yLog,
1110                                            DDouble xStart,
1111                                            DDouble xEnd,
1112                                            DDouble yStart,
1113                                            DDouble yEnd, DDouble zStart=0.0, DDouble zEnd=1.0, bool zLog=false)
1114   {
1115 
1116    // set ![XY].CRANGE Before doing anything relative to 3D.
1117     gdlStoreAxisCRANGE(XAXIS, xStart, xEnd, xLog);
1118     gdlStoreAxisCRANGE(YAXIS, yStart, yEnd, yLog);
1119     gdlStoreAxisCRANGE(ZAXIS, zStart, zEnd, zLog);
1120     //set ![XY].type
1121     gdlStoreAxisType(XAXIS,xLog);
1122     gdlStoreAxisType(YAXIS,yLog);
1123     gdlStoreAxisType(ZAXIS,zLog);
1124     //set ![XY].WINDOW and ![XY].S
1125     gdlStoreAxisSandWINDOW(actStream, XAXIS, xStart, xEnd, xLog);
1126     gdlStoreAxisSandWINDOW(actStream, YAXIS, yStart, yEnd, yLog);
1127     gdlStoreAxisSandWINDOW(actStream, ZAXIS, zStart, zEnd, zLog);
1128 
1129     //3D work
1130     enum{ DATA=0,
1131           NORMAL,
1132           DEVICE
1133         } coordinateSystem=DATA;
1134     //To center plot, compute projected corners of 1 unit box
1135     static DDouble zz[8]={0,0,0,0,1,1,1,1};
1136     static DDouble yy[8]={0,0,1,1,0,0,1,1};
1137     static DDouble xx[8]={0,1,0,1,0,1,0,1};
1138     static DDouble ww[8]={1,1,1,1,1,1,1,1};
1139 
1140     DDoubleGDL* V=(new DDoubleGDL(dimension(8,4)));
1141     memcpy(&((*V)[0]),xx,8*sizeof(double));
1142     memcpy(&((*V)[8]),yy,8*sizeof(double));
1143     memcpy(&((*V)[16]),zz,8*sizeof(double));
1144     memcpy(&((*V)[24]),ww,8*sizeof(double));
1145 
1146     DDoubleGDL* pV=(Matrix->MatrixOp(V,false,true));
1147 
1148     DDouble xmin,xmax,ymin,ymax;
1149     DLong iMin,iMax;
1150     pV->MinMax(&iMin,&iMax,NULL,NULL,false,0,0,4);
1151     xmin=(*pV)[iMin];
1152     xmax=(*pV)[iMax];
1153     pV->MinMax(&iMin,&iMax,NULL,NULL,false,1,0,4);
1154     ymin=(*pV)[iMin];
1155     ymax=(*pV)[iMax];
1156 
1157     PLFLT xMR, xML, yMB, yMT;
1158     DFloat xMarginL, xMarginR, yMarginB, yMarginT;
1159     gdlGetDesiredAxisMargin(e, XAXIS, xMarginL, xMarginR);
1160     gdlGetDesiredAxisMargin(e, YAXIS, yMarginB, yMarginT);
1161     PLFLT scl=actStream->nCharLength(); //current char width
1162     xML=xMarginL*scl; //margin as percentage of subpage
1163     xMR=xMarginR*scl;
1164     scl=actStream->nCharHeight(); //current char height
1165     yMB=(yMarginB)*scl;
1166     yMT=(yMarginT)*scl;
1167 
1168     if ( xML+xMR>=1.0 )
1169     {
1170       PLFLT xMMult=xML+xMR;
1171       xML/=xMMult*1.5;
1172       xMR/=xMMult*1.5;
1173     }
1174     if ( yMB+yMT>=1.0 )
1175     {
1176       PLFLT yMMult=yMB+yMT;
1177       yMB/=yMMult*1.5;
1178       yMT/=yMMult*1.5;
1179     }
1180 
1181     static PLFLT positionP[4]={0, 0, 0, 0};
1182     static PLFLT regionP[4]={0, 0, 0, 0};
1183     static PLFLT position[4]={0,0,1,1};
1184     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
1185     // Get !P.position values. !P.REGION is superseded by !P.POSITION
1186     if ( pStruct!=NULL )
1187     {
1188 
1189       unsigned regionTag=pStruct->Desc()->TagIndex("REGION");
1190       for ( SizeT i=0; i<4; ++i ) regionP[i]=(PLFLT)(*static_cast<DFloatGDL*>(pStruct->GetTag(regionTag, 0)))[i];
1191       unsigned positionTag=pStruct->Desc()->TagIndex("POSITION");
1192       for ( SizeT i=0; i<4; ++i ) positionP[i]=(PLFLT)(*static_cast<DFloatGDL*>(pStruct->GetTag(positionTag, 0)))[i];
1193     }
1194     if (regionP[0]!=regionP[2] && positionP[0]==positionP[2]) //if not ignored, and will be used, as
1195                 //a surrogate of !P.Position:
1196     {
1197         //compute position removing margins
1198         positionP[0]=regionP[0]+xMarginL*actStream->nCharLength();
1199         positionP[1]=regionP[1]+yMarginB*actStream->nCharHeight();
1200         positionP[2]=regionP[2]-xMarginR*actStream->nCharLength();
1201         positionP[3]=regionP[3]-yMarginT*actStream->nCharHeight();
1202     }
1203     //compatibility: Position NEVER outside [0,1]:
1204     positionP[0]=max(0.0,positionP[0]);
1205     positionP[1]=max(0.0,positionP[1]);
1206     positionP[2]=min(1.0,positionP[2]);
1207     positionP[3]=min(1.0,positionP[3]);
1208 
1209     //check presence of DATA,DEVICE and NORMAL options
1210     static int DATAIx=e->KeywordIx("DATA");
1211     static int DEVICEIx=e->KeywordIx("DEVICE");
1212     static int NORMALIx=e->KeywordIx("NORMAL");
1213 
1214     if (e->KeywordSet(DATAIx)) coordinateSystem = DATA;
1215     if (e->KeywordSet(DEVICEIx)) coordinateSystem = DEVICE;
1216     if (e->KeywordSet(NORMALIx)) coordinateSystem = NORMAL;
1217 //    if (coordinateSystem==DATA && !actStream->validWorldBox()) e->Throw("PLOT: Data coordinate system not established.");
1218     // read boxPosition if needed
1219     static int positionIx = e->KeywordIx( "POSITION");
1220     DFloatGDL* boxPosition = e->IfDefGetKWAs<DFloatGDL>( positionIx);
1221     if (boxPosition == NULL) boxPosition = (DFloatGDL*) 0xF;
1222     if ( boxPosition!=(DFloatGDL*)0xF)
1223     {
1224       for ( SizeT i=0; i<4&&i<boxPosition->N_Elements(); ++i ) position[i]=(*boxPosition)[i];
1225     }
1226     // modify positionP and/or boxPosition to NORMAL if DEVICE is present
1227     if (coordinateSystem==DEVICE)
1228     {
1229       PLFLT normx;
1230       PLFLT normy;
1231       actStream->DeviceToNormedDevice(positionP[0], positionP[1], normx, normy);
1232       positionP[0]=normx;
1233       positionP[1]=normy;
1234       actStream->DeviceToNormedDevice(positionP[2], positionP[3], normx, normy);
1235       positionP[2]=normx;
1236       positionP[3]=normy;
1237       if ( boxPosition!=(DFloatGDL*)0xF)
1238       {
1239         actStream->DeviceToNormedDevice(position[0], position[1], normx, normy);
1240         position[0]=normx;
1241         position[1]=normy;
1242         actStream->DeviceToNormedDevice(position[2], position[3], normx, normy);
1243         position[2]=normx;
1244         position[3]=normy;
1245       }
1246     }
1247     if ( boxPosition!=(DFloatGDL*)0xF)
1248     {    //compatibility again: Position NEVER outside [0,1]:
1249       position[0]=max(0.0,position[0]);
1250       position[1]=max(0.0,position[1]);
1251       position[2]=min(1.0,position[2]);
1252       position[3]=min(1.0,position[3]);
1253     }
1254 
1255     // New plot without POSITION=[] as argument
1256     if ( boxPosition==(DFloatGDL*)0xF )
1257     {
1258       // If !P.position not set use default values. coordinatesSystem not used even if present!
1259       if ( positionP[0]==0&&positionP[1]==0&&
1260            positionP[2]==0&&positionP[3]==0 )
1261       {
1262         // Set to (smart?) default values
1263         position[0]=0;
1264         position[1]=0+2*(yMB/yMarginB); //subtitle
1265         position[2]=1.0;
1266         position[3]=1.0-2*(yMT/yMarginT); //title
1267         actStream->vpor(position[0], position[2], position[1], position[3]);
1268       }
1269       else
1270       {
1271         // Use !P.position values.
1272         actStream->vpor(positionP[0], positionP[2], positionP[1], positionP[3]);
1273      }
1274     }
1275     else // Position keyword set
1276     {
1277       actStream->vpor(position[0], position[2], position[1], position[3]);
1278     }
1279     //adjust 'world' values to give room to axis labels. Could be better if we take
1280     //into account projection angles
1281     // fix word values without labels:
1282     actStream->wind(xmin, xmax, ymin, ymax);
1283     //compute world Charsize
1284     PLFLT xb, xe, yb, ye;
1285     xb=xmin-xMarginL*actStream->wCharLength();
1286     xe=xmax+xMarginR*actStream->wCharLength();
1287     yb=ymin-yMarginB*actStream->wCharHeight();
1288     ye=ymax-yMarginT*actStream->wCharHeight();
1289     actStream->wind(xb, xe, yb, ye);
1290 
1291 
1292     //Clipping is false in 3D...
1293 
1294     //set P.CLIP (done by PLOT, CONTOUR, SHADE_SURF, and SURFACE)
1295     Guard<BaseGDL> clipbox_guard;
1296     DLongGDL* clipBox= new DLongGDL(4, BaseGDL::ZERO); clipbox_guard.Reset(clipBox);
1297     PLFLT x,y;
1298     actStream->gvpd(xmin, xmax, ymin, ymax);
1299 
1300     actStream->NormedDeviceToDevice(xmin, ymin, x,y);
1301     (*clipBox)[0]=x;
1302     (*clipBox)[1]=y;
1303     actStream->NormedDeviceToDevice(xmax, ymax,x,y);
1304     (*clipBox)[2]=x;
1305     (*clipBox)[3]=y;
1306     gdlStoreCLIP(clipBox);
1307     return true;
1308   }
1309     //TODO: put margin discovery in gdlSetViewPortAndWorldCoordinates (simplify call list)
1310   //also, solve the proble of passing back xStart etc if they are changed by unwantedaxisvalue())
1311 
gdlSetViewPortAndWorldCoordinates(EnvT * e,GDLGStream * actStream,bool xLog,bool yLog,DFloat xMarginL,DFloat xMarginR,DFloat yMarginB,DFloat yMarginT,DDouble xStart,DDouble xEnd,DDouble yStart,DDouble yEnd,DLong iso)1312   static bool gdlSetViewPortAndWorldCoordinates(EnvT* e,
1313                                          GDLGStream* actStream,
1314                                          bool xLog, bool yLog,
1315                                          DFloat xMarginL,
1316                                          DFloat xMarginR,
1317                                          DFloat yMarginB,
1318                                          DFloat yMarginT,
1319                                          DDouble xStart,
1320                                          DDouble xEnd,
1321                                          DDouble yStart,
1322                                          DDouble yEnd,
1323                                          DLong iso)
1324   {
1325 
1326     PLFLT xMR;
1327     PLFLT xML;
1328     PLFLT yMB;
1329     PLFLT yMT;
1330     enum{ DATA=0,
1331           NORMAL,
1332           DEVICE
1333         } coordinateSystem=DATA;
1334 
1335     CheckMargin(actStream,
1336                 xMarginL,
1337                 xMarginR,
1338                 yMarginB,
1339                 yMarginT,
1340                 xMR, xML, yMB, yMT);
1341 
1342     // viewport - POSITION overrides
1343     static bool kwP=FALSE;
1344     static bool do_iso=FALSE;
1345     static PLFLT aspect=0.0;
1346 
1347     static PLFLT positionP[4]={0, 0, 0, 0};
1348     static PLFLT regionP[4]={0, 0, 0, 0};
1349     static PLFLT position[4]={0,0,1,1};
1350     DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
1351     // Get !P.position values. !P.REGION is superseded by !P.POSITION
1352     if ( pStruct!=NULL )
1353     {
1354 
1355       unsigned regionTag=pStruct->Desc()->TagIndex("REGION");
1356       for ( SizeT i=0; i<4; ++i ) regionP[i]=(PLFLT)(*static_cast<DFloatGDL*>(pStruct->GetTag(regionTag, 0)))[i];
1357       unsigned positionTag=pStruct->Desc()->TagIndex("POSITION");
1358       for ( SizeT i=0; i<4; ++i ) positionP[i]=(PLFLT)(*static_cast<DFloatGDL*>(pStruct->GetTag(positionTag, 0)))[i];
1359     }
1360     if (regionP[0]!=regionP[2] && positionP[0]==positionP[2]) //if not ignored, and will be used, as
1361                 //a surrogate of !P.Position:
1362     {
1363         //compute position removing margins
1364         positionP[0]=regionP[0]+xMarginL*actStream->nCharLength();
1365         positionP[1]=regionP[1]+yMarginB*actStream->nLineSpacing();
1366         positionP[2]=regionP[2]-xMarginR*actStream->nCharLength();
1367         positionP[3]=regionP[3]-yMarginT*actStream->nLineSpacing();
1368     }
1369     //compatibility: Position NEVER outside [0,1]:
1370     positionP[0]=max(0.0,positionP[0]);
1371     positionP[1]=max(0.0,positionP[1]);
1372     positionP[2]=min(1.0,positionP[2]);
1373     positionP[3]=min(1.0,positionP[3]);
1374 
1375     //check presence of DATA,DEVICE and NORMAL options
1376     static int DATAIx=e->KeywordIx("DATA");
1377     static int DEVICEIx=e->KeywordIx("DEVICE");
1378     static int NORMALIx=e->KeywordIx("NORMAL");
1379 
1380     if (e->KeywordSet(DATAIx)) coordinateSystem = DATA;
1381     if (e->KeywordSet(DEVICEIx)) coordinateSystem = DEVICE;
1382     if (e->KeywordSet(NORMALIx)) coordinateSystem = NORMAL;
1383 //    if (coordinateSystem==DATA && !actStream->validWorldBox()) e->Throw("PLOT: Data coordinate system not established.");
1384     // read boxPosition if needed
1385     static int positionIx = e->KeywordIx( "POSITION");
1386     DFloatGDL* boxPosition = e->IfDefGetKWAs<DFloatGDL>( positionIx);
1387     if (boxPosition == NULL) boxPosition = (DFloatGDL*) 0xF;
1388     if ( boxPosition!=NULL && boxPosition!=(DFloatGDL*)0xF )
1389     {
1390       for ( SizeT i=0; i<4&&i<boxPosition->N_Elements(); ++i ) position[i]=(*boxPosition)[i];
1391     }
1392     // modify positionP and/or boxPosition to NORMAL if DEVICE is present
1393     if (coordinateSystem==DEVICE)
1394     {
1395       PLFLT normx;
1396       PLFLT normy;
1397       actStream->DeviceToNormedDevice(positionP[0], positionP[1], normx, normy);
1398       positionP[0]=normx;
1399       positionP[1]=normy;
1400       actStream->DeviceToNormedDevice(positionP[2], positionP[3], normx, normy);
1401       positionP[2]=normx;
1402       positionP[3]=normy;
1403       if ( boxPosition!=NULL && boxPosition!=(DFloatGDL*)0xF )
1404       {
1405         actStream->DeviceToNormedDevice(position[0], position[1], normx, normy);
1406         position[0]=normx;
1407         position[1]=normy;
1408         actStream->DeviceToNormedDevice(position[2], position[3], normx, normy);
1409         position[2]=normx;
1410         position[3]=normy;
1411      }
1412     }
1413     if ( boxPosition!=NULL && boxPosition!=(DFloatGDL*)0xF )
1414     {
1415        //compatibility again: Position NEVER outside [0,1]:
1416       position[0]=max(0.0,position[0]);
1417       position[1]=max(0.0,position[1]);
1418       position[2]=min(1.0,position[2]);
1419       position[3]=min(1.0,position[3]);
1420     }
1421     // Adjust Start and End for Log (convert to log)
1422     if ( boxPosition!=NULL ) //new box
1423     {
1424       if ( xLog )
1425       {
1426         gdlHandleUnwantedAxisValue(xStart, xEnd, xLog);
1427         xStart=log10(xStart);
1428         xEnd=log10(xEnd);
1429       }
1430       if ( yLog )
1431       {
1432         gdlHandleUnwantedAxisValue(yStart, yEnd, yLog);
1433         yStart=log10(yStart);
1434         yEnd=log10(yEnd);
1435       }
1436     }
1437     // If pos == NULL (oplot, /OVERPLOT etc: Reuse previous values)
1438     if ( boxPosition==NULL )
1439     {
1440       // If position keyword previously set
1441       if ( kwP )
1442       {
1443         // Creates a viewport with the specified normalized subpage coordinates.
1444         if ( do_iso ) setIsoPort(actStream, position[0], position[2], position[1], position[3], aspect);
1445         else actStream->vpor(position[0], position[2], position[1], position[3]);
1446       }
1447       else
1448       {
1449         // If !P.position not set
1450         if ( positionP[0]==0&&positionP[1]==0&&
1451              positionP[2]==0&&positionP[3]==0 )
1452         {
1453           if ( do_iso ) setIsoPort(actStream, position[0], position[2], position[1], position[3], aspect);
1454           else actStream->vpor(position[0], position[2], position[1], position[3]);
1455         }
1456         else
1457         {
1458           // !P.position set
1459           if ( do_iso ) setIsoPort(actStream, positionP[0], positionP[2], positionP[1], positionP[3], aspect);
1460           else actStream->vpor(positionP[0], positionP[2], positionP[1], positionP[3]);
1461         }
1462       }
1463     }
1464     else //New Plot
1465     {
1466       if ( iso==1 ) // Check ISOTROPIC first
1467       {
1468         do_iso=TRUE;
1469         aspect=abs((yEnd-yStart)/(xEnd-xStart)); //log-log or lin-log
1470       }
1471       else
1472       {
1473         do_iso=FALSE;
1474         aspect=0.0; // vpas with aspect=0.0 equals vpor.
1475       }
1476 
1477       // New plot without POSITION=[] as argument
1478       if ( boxPosition==(DFloatGDL*)0xF )
1479       {
1480         kwP=false;
1481         // If !P.position not set use default values. coordinatesSystem not used even if present!
1482         if ( positionP[0]==0&&positionP[1]==0&&
1483              positionP[2]==0&&positionP[3]==0 )
1484         {
1485 
1486           // Set to default values
1487           position[0]=xML;
1488           position[1]=yMB;
1489           position[2]=1.0-xMR;
1490           position[3]=1.0-yMT;
1491           if ( do_iso ) setIsoPort(actStream, position[0], position[2], position[1], position[3], aspect);
1492           else actStream->vpor(position[0], position[2], position[1], position[3]);
1493         }
1494         else
1495         {
1496           // Use !P.position values.
1497           if ( do_iso ) setIsoPort(actStream, positionP[0], positionP[2], positionP[1], positionP[3], aspect);
1498           else actStream->vpor(positionP[0], positionP[2], positionP[1], positionP[3]);
1499         }
1500       }
1501       else // Position keyword set
1502       {
1503         kwP=true;
1504         if ( do_iso ) setIsoPort(actStream, position[0], position[2], position[1], position[3], aspect);
1505         else actStream->vpor(position[0], position[2], position[1], position[3]);
1506       }
1507     }
1508 
1509     // for OPLOT start and end values are already log
1510     // SA: changing only local variables!
1511 
1512     //cout << "VP wind: "<<xStart<<" "<<xEnd<<" "<<yStart<<" "<<yEnd<<endl;
1513     //printf("data lim (setv): %f %f %f %f\n", xStart, xEnd, yStart, yEnd);
1514     // set world coordinates
1515     //protection against silly coordinates
1516     if (xStart==xEnd)
1517     {
1518       Message(e->GetProName()+"Coordinate system in error, please report to authors.");
1519       xStart=0.0;
1520       xEnd=1.0;
1521     }
1522     if (yStart==yEnd)
1523     {
1524       Message(e->GetProName()+"Coordinate system in error, please report to authors.");
1525       yStart=0.0;
1526       yEnd=1.0;
1527     }
1528     actStream->wind(xStart, xEnd, yStart, yEnd);
1529     //       cout << "xStart " << xStart << "  xEnd "<<xEnd<<endl;
1530     //        cout << "yStart " << yStart << "  yEnd "<<yEnd<<endl;
1531 
1532     // set ![XYZ].CRANGE (Z is not defined but must be [0,1])
1533     gdlStoreAxisCRANGE(XAXIS, xStart, xEnd, FALSE); //already in log here if relevant!
1534     gdlStoreAxisCRANGE(YAXIS, yStart, yEnd, FALSE);
1535 
1536     //set ![XY].type
1537     gdlStoreAxisType(XAXIS,xLog);
1538     gdlStoreAxisType(YAXIS,yLog);
1539 
1540     //set ![XY].WINDOW and ![XY].S
1541     gdlStoreAxisSandWINDOW(actStream, XAXIS, xStart, xEnd, FALSE);//already in log here if relevant!
1542     gdlStoreAxisSandWINDOW(actStream, YAXIS, yStart, yEnd, FALSE);
1543     //set P.CLIP (done by PLOT, CONTOUR, SHADE_SURF, and SURFACE)
1544     Guard<BaseGDL> clipbox_guard;
1545     DLongGDL* clipBox= new DLongGDL(4, BaseGDL::ZERO); clipbox_guard.Reset(clipBox);
1546     PLFLT xmin, xmax, ymin, ymax, x,y;
1547     actStream->gvpd(xmin, xmax, ymin, ymax);
1548 
1549     actStream->NormedDeviceToDevice(xmin, ymin, x,y);
1550     (*clipBox)[0]=x;
1551     (*clipBox)[1]=y;
1552     actStream->NormedDeviceToDevice(xmax, ymax,x,y);
1553     (*clipBox)[2]=x;
1554     (*clipBox)[3]=y;
1555     gdlStoreCLIP(clipBox);
1556     return true;
1557   }
1558 
startClipping(EnvT * e,GDLGStream * a,bool canUsePClip=false)1559   static bool startClipping(EnvT *e, GDLGStream *a, bool canUsePClip=false)
1560   {
1561     if (GDL_DEBUG_PLSTREAM)  fprintf(stderr,"startClipping\n");
1562     //function to be called when clipping must be actived, i.e., if the combination of CLIP= and NOCLIP= necessitate it
1563     //the function retrieves the pertinent information in keywords
1564     enum
1565     {
1566       DATA=0,
1567       NORMAL,
1568       DEVICE
1569     } coordinateSystem=DATA;
1570     bool xinverted=FALSE;
1571     bool yinverted=FALSE; //for inverted DATA coordinates
1572 
1573 
1574 
1575     static int clippingix=e->KeywordIx("CLIP");
1576     DFloatGDL* clipBox=NULL;
1577     clipBox=e->IfDefGetKWAs<DFloatGDL>(clippingix);
1578 
1579     //Get saveBox
1580     gdlSavebox* saveBox=getSaveBox();
1581     //Save current box
1582     a->gvpd(saveBox->nx1, saveBox->nx2, saveBox->ny1, saveBox->ny2); //save norm of current box
1583     a->gvpw(saveBox->wx1, saveBox->wx2, saveBox->wy1, saveBox->wy2); //save world of current box
1584     saveBox->initialized=true; //mark as initialized (debug complicated clipping algo)
1585     //test axis inversion
1586     xinverted=(saveBox->wx1>saveBox->wx2);
1587     yinverted=(saveBox->wy1>saveBox->wy2);
1588     //GET CLIPPING
1589     PLFLT dClipBox[4]={0, 0, 0, 0};
1590     PLFLT tempbox[4]={0, 0, 0, 0};
1591     DDouble un, deux, trois, quatre;
1592     static int NOCLIPIx=e->KeywordIx("NOCLIP");
1593     static string proname=e->GetProName();
1594     static bool invertedMeaning=(proname=="PLOTS"||proname=="POLYFILL"||proname=="XYOUTS");
1595     int noclipvalue=1;
1596     e->AssureLongScalarKWIfPresent( NOCLIPIx, noclipvalue);
1597     bool willNotClip;
1598     //eliminate simple cases
1599     if (invertedMeaning) willNotClip=(noclipvalue==1); else willNotClip=(e->KeywordSet(NOCLIPIx));
1600     if (willNotClip) return false;
1601     if ( !canUsePClip  && clipBox==NULL ) return false;
1602     if ( !canUsePClip  && clipBox->N_Elements()<4) return false;
1603     //now we can start checking more deeply
1604     if (proname != "OPLOT") {     //OPLOT has no /DEVICE /NORM and is already /DATA
1605      static int DATAIx=e->KeywordIx("DATA");
1606      static int DEVICEIx=e->KeywordIx("DEVICE");
1607      static int NORMALIx=e->KeywordIx("NORMAL");
1608 
1609      if (e->KeywordSet(DATAIx)) coordinateSystem = DATA;
1610      if (e->KeywordSet(DEVICEIx)) coordinateSystem = DEVICE;
1611      if (e->KeywordSet(NORMALIx)) coordinateSystem = NORMAL;
1612     }
1613 
1614     if ( clipBox==NULL && canUsePClip ) //get !P.CLIP. Coordinates are always DEVICE
1615       {
1616         DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
1617         unsigned clipTag=pStruct->Desc()->TagIndex("CLIP"); //is in device coordinates
1618         for ( int i=0; i<4; ++i ) tempbox[i]=dClipBox[i]=(*static_cast<DLongGDL*>(pStruct->GetTag(clipTag, 0)))[i];
1619         coordinateSystem = DEVICE; //is in device coordinates
1620         if (GDL_DEBUG_PLSTREAM) fprintf(stderr, "using !P.CLIP=[%f,%f,%f,%f]\n", dClipBox[0], dClipBox[1], dClipBox[2], dClipBox[3]);
1621       }
1622       else //get units, convert to world coords for plplot, take care of axis direction
1623       {
1624         if ( (*clipBox)[0]>=(*clipBox)[2] ||(*clipBox)[1]>=(*clipBox)[3] ) {
1625          coordinateSystem=NORMAL;
1626          tempbox[0]=0.0;
1627          tempbox[1]=0.0;
1628          tempbox[2]=0.00001; //ridiculous but works.
1629          tempbox[3]=0.00001;
1630         } else for ( int i=0; i<4&&i<clipBox->N_Elements(); ++i ) tempbox[i]=dClipBox[i]=(*clipBox)[i];
1631 
1632         if (GDL_DEBUG_PLSTREAM) fprintf(stderr, "using given CLIP=[%f,%f,%f,%f]\n", dClipBox[0], dClipBox[1], dClipBox[2], dClipBox[3]);
1633         if ( coordinateSystem==DATA )
1634         {
1635           int *tx,*ty;
1636           int txn[2]={0,2};
1637           int txr[2]={2,0};
1638           int tyn[2]={1,3};
1639           int tyr[2]={3,1};
1640           if(tempbox[0]<tempbox[2]) { if (xinverted) tx=txr; else tx=txn;} else { if (xinverted) tx=txn; else tx=txr;}
1641           if(tempbox[1]<tempbox[3]) { if (yinverted) ty=tyr; else ty=tyn;} else { if (yinverted) ty=tyn; else ty=tyr;}
1642           un=tempbox[tx[0]];
1643           deux=tempbox[ty[0]];
1644           a->WorldToDevice(un, deux, trois, quatre);
1645           dClipBox[0]=trois;
1646           dClipBox[1]=quatre;
1647           un=tempbox[tx[1]];
1648           deux=tempbox[ty[1]];
1649           a->WorldToDevice(un, deux, trois, quatre);
1650           dClipBox[2]=trois;
1651           dClipBox[3]=quatre;
1652         }
1653         else if ( coordinateSystem==NORMAL )
1654         {
1655           a->NormToDevice(tempbox[0], tempbox[1], dClipBox[0], dClipBox[1]);
1656           a->NormToDevice(tempbox[2], tempbox[3], dClipBox[2], dClipBox[3]);
1657         }
1658       }
1659     // we are now in DEVICE Coords
1660 //    }
1661     //if new box is in error, return it:
1662     if (dClipBox[0]>=dClipBox[2]||dClipBox[1]>=dClipBox[3]) return false;
1663     //compute and set corresponding world coords before using whole page:
1664     a->DeviceToWorld(dClipBox[0], dClipBox[1],tempbox[0], tempbox[1]);
1665     a->DeviceToWorld(dClipBox[2], dClipBox[3],tempbox[2], tempbox[3]);
1666 
1667     a->NoSub();
1668     // set full page viewport for the clip box boundaries:
1669     PLFLT xmin,xmax,ymin,ymax;
1670     a->DeviceToNormedDevice(dClipBox[0], dClipBox[1],xmin, ymin);
1671     a->DeviceToNormedDevice(dClipBox[2], dClipBox[3],xmax, ymax);
1672     a->vpor(xmin, xmax,ymin, ymax);
1673     a->wind(tempbox[0], tempbox[2], tempbox[1], tempbox[3]);
1674 //    a->box( "bc", 0, 0, "bc", 0.0, 0);
1675     return TRUE;
1676   }
gdlAxis(EnvT * e,GDLGStream * a,int axisId,DDouble Start,DDouble End,bool Log,DLong modifierCode=0,DDouble NormedLength=0)1677     static bool gdlAxis(EnvT *e, GDLGStream *a, int axisId, DDouble Start, DDouble End, bool Log,
1678     DLong modifierCode=0, DDouble NormedLength=0)
1679   {
1680     static GDL_TICKDATA tdata;
1681     tdata.a=a;
1682     tdata.isLog=Log;
1683     tdata.axisrange=abs(End-Start);
1684 
1685     static GDL_TICKNAMEDATA data;
1686     data.a=a;
1687     data.isLog=Log;
1688     data.axisrange=abs(End-Start);
1689     data.nTickName=0;
1690 
1691     static GDL_MULTIAXISTICKDATA muaxdata;
1692     muaxdata.e=e;
1693     muaxdata.a=a;
1694     muaxdata.isLog=Log;
1695     muaxdata.what=GDL_NONE;
1696     muaxdata.nTickFormat=0;
1697     muaxdata.nTickUnits=0;
1698     muaxdata.axismin=Start;
1699     muaxdata.axismax=End;
1700     muaxdata.axisrange=abs(End-Start);
1701     muaxdata.reset=true;
1702 
1703     //special values
1704     PLFLT OtherAxisSizeInMm;
1705     if (axisId==XAXIS) OtherAxisSizeInMm=a->mmyPageSize()*(a->boxnYSize());
1706     if (axisId==YAXIS) OtherAxisSizeInMm=a->mmxPageSize()*(a->boxnXSize());
1707     //special for AXIS who change the requested box size!
1708     if (axisId==XAXIS2) {axisId=XAXIS; OtherAxisSizeInMm=a->mmyPageSize()*(NormedLength);}
1709     if (axisId==YAXIS2) {axisId=YAXIS; OtherAxisSizeInMm=a->mmxPageSize()*(NormedLength);}
1710 
1711     DLong GridStyle;
1712     gdlGetDesiredAxisGridStyle(e, axisId, GridStyle);
1713     DLong Minor;
1714     gdlGetDesiredAxisMinor(e, axisId, Minor);
1715     DLong Style;
1716     gdlGetDesiredAxisStyle(e, axisId, Style);
1717     DFloat Thick;
1718     gdlGetDesiredAxisThick(e, axisId, Thick);
1719     DStringGDL* TickFormat;
1720     gdlGetDesiredAxisTickFormat(e, axisId, TickFormat);
1721     DDouble TickInterval;
1722     gdlGetDesiredAxisTickInterval(e, axisId, TickInterval);
1723     DLong TickLayout;
1724     gdlGetDesiredAxisTickLayout(e, axisId, TickLayout);
1725     DFloat TickLen;
1726     gdlGetDesiredAxisTickLen(e, axisId, TickLen);
1727     DStringGDL* TickName;
1728     gdlGetDesiredAxisTickName(e, a, axisId, TickName);
1729     DLong Ticks;
1730     gdlGetDesiredAxisTicks(e, axisId, Ticks);
1731     DStringGDL* TickUnits;
1732     gdlGetDesiredAxisTickUnits(e, axisId, TickUnits);
1733 //    DDoubleGDL *Tickv;
1734 //    gdlGetDesiredAxisTickv(e, axisId, Tickv);
1735     DString Title;
1736     gdlGetDesiredAxisTitle(e, axisId, Title);
1737 
1738     bool hasTickUnitDefined = (TickUnits->NBytes()>0);
1739     int tickUnitArraySize=(hasTickUnitDefined)?TickUnits->N_Elements():0;
1740 
1741     //For labels we need ticklen in current character size, for ticks we need it in mm
1742     DFloat ticklen_in_mm= TickLen;
1743     if (TickLen<0) ticklen_in_mm*=-1;
1744     //ticklen in a percentage of box x or y size, to be expressed in mm
1745     if (axisId==XAXIS) ticklen_in_mm=a->mmyPageSize()*(a->boxnYSize())*ticklen_in_mm;
1746     if (axisId==YAXIS) ticklen_in_mm=a->mmxPageSize()*(a->boxnXSize())*ticklen_in_mm;
1747     DFloat ticklen_as_norm=(axisId==XAXIS)?a->mm2ndy(ticklen_in_mm):a->mm2ndx(ticklen_in_mm); //in normed coord
1748     //eventually, each succesive X or Y axis is separated from previous by interligne + ticklen in adequate units.
1749     DFloat interligne_as_char;
1750     DFloat interligne_as_norm;
1751     DFloat typical_char_size_mm= (axisId==XAXIS)?a->mmCharHeight():a->mmCharLength();
1752     interligne_as_char=(axisId==XAXIS)?a->mmLineSpacing()/typical_char_size_mm:a->mmCharLength()/typical_char_size_mm; //in normed coord
1753     interligne_as_norm=(axisId==XAXIS)?a->nLineSpacing():a->nCharLength(); //in normed coord
1754     DFloat displacement_of_new_axis_as_norm=2*interligne_as_norm+ticklen_as_norm;
1755     DFloat current_displacement=0;
1756     DFloat title_position=0;
1757     if ( (Style&4)!=4 ) //if we write the axis...
1758     {
1759       double nchars; //max number of chars written in label of axis.
1760       string Opt;
1761       string otherOpt;
1762       if (TickInterval==0)
1763       {
1764         if (Ticks<=0) TickInterval=gdlComputeTickInterval(e, axisId, Start, End, Log);
1765         else if (Ticks>1) TickInterval=(End-Start)/Ticks;
1766         else TickInterval=(End-Start);
1767       } else { //check that tickinterval does not make more than 59 ticks:
1768        if (abs((End-Start)/TickInterval) > 59) TickInterval=(End-Start)/59;
1769       }
1770       //first write labels only:
1771       gdlSetAxisCharsize(e, a, axisId);
1772       gdlSetPlotCharthick(e, a);
1773 
1774       //axis, 1st time: labels
1775       Opt="tvx";otherOpt="tv"; //draw major ticks "t" + v:values perp to Y axis + x:
1776       // the x option is in plplot 5.9.8 but not before. It permits
1777                 // to avoid writing tick marks here (they will be written after)
1778                 // I hope old plplots were clever enough to ignore 'x'
1779                 // if they did not understand 'x'
1780       if ( Log ) Opt+="l"; //"l" for log; otherOpt is never in log I believe
1781       if (TickName->NBytes()>0) // /TICKNAME=[array]
1782       {
1783         data.counter=0;
1784         data.nchars=0;
1785         data.TickName=TickName;
1786         data.nTickName=TickName->N_Elements();
1787         a->slabelfunc( gdlSingleAxisTickNamedFunc, &data );
1788         Opt+="o";
1789         if (modifierCode==2) Opt+="m"; else Opt+="n";
1790         if (axisId==XAXIS) a->box(Opt.c_str(), TickInterval, Minor, "", 0.0, 0);
1791         else if (axisId==YAXIS) a->box("", 0.0 ,0.0, Opt.c_str(), TickInterval, Minor);
1792         nchars=data.nchars;
1793         if (axisId==YAXIS) title_position=nchars+2.5; else title_position=3.5;
1794         a->slabelfunc( NULL, NULL );
1795       }
1796       //care Tickunits size is 10 if not defined because it is the size of !X.TICKUNITS.
1797       else if (hasTickUnitDefined) // /TICKUNITS=[several types of axes written below each other]
1798       {
1799         muaxdata.counter=0;
1800         muaxdata.what=GDL_TICKUNITS;
1801         if (TickFormat->NBytes()>0)  // with also TICKFORMAT option..
1802         {
1803           muaxdata.what=GDL_TICKFORMAT_AND_UNITS;
1804           muaxdata.TickFormat=TickFormat;
1805           muaxdata.nTickFormat=TickFormat->N_Elements();
1806         }
1807         muaxdata.TickUnits=TickUnits;
1808         muaxdata.nTickUnits=tickUnitArraySize;
1809         a->slabelfunc( gdlMultiAxisTickFunc, &muaxdata );
1810         Opt+="o";otherOpt+="o"; //use external func custom labeling
1811         if (modifierCode==2) {Opt+="m"; otherOpt+="m";} else {Opt+="n"; otherOpt+="n";} //m: write numerical/right above, n: below/left (normal)
1812         PLFLT un,deux,trois,quatre,xun,xdeux,xtrois,xquatre;
1813         a->getCurrentNormBox(un,deux,trois,quatre);
1814         a->getCurrentWorldBox(xun,xdeux,xtrois,xquatre);
1815         a->smaj(ticklen_in_mm, 1.0 );
1816         for (SizeT i=0; i< muaxdata.nTickUnits; ++i) //loop on TICKUNITS axis
1817         {
1818           muaxdata.nchars=0; //set nchars to 0, at the end nchars will be the maximum size.
1819           if (i>0) Opt=otherOpt+"b"; //supplementary axes are to be wwritten with ticks, no smallticks;
1820           if (axisId==XAXIS)
1821           {
1822             a->vpor(un,deux,trois-current_displacement,quatre);
1823             a->wind(xun,xdeux,xtrois,xquatre);
1824             a->box(Opt.c_str(), TickInterval, Minor, "", 0.0, 0); //to avoid plplot crashes: do not use tickinterval. or recompute it correctly (no too small!)
1825             title_position=current_displacement/a->nCharHeight()+3.5;
1826             current_displacement+=displacement_of_new_axis_as_norm; //and the spacing plus the ticklengths
1827           }
1828           else if (axisId==YAXIS)
1829           {
1830             a->vpor(un-current_displacement,deux,trois,quatre);
1831             a->wind(xun,xdeux,xtrois,xquatre);
1832             a->box("", 0.0 ,0.0, Opt.c_str(), TickInterval, Minor); //to avoid plplot crashes: do not use tickinterval. or recompute it correctly (no too small!)
1833             nchars=muaxdata.nchars;
1834             title_position=current_displacement/a->nCharLength()+nchars+2.5;
1835             current_displacement+=(nchars-1)*a->nCharLength(); //we'll skip what was written
1836             current_displacement+=displacement_of_new_axis_as_norm; //and the spacing plus the ticklengths
1837           }
1838           muaxdata.counter++;
1839         }
1840         a->vpor(un,deux,trois,quatre);
1841         a->wind(xun,xdeux,xtrois,xquatre);
1842         a->slabelfunc( NULL, NULL );
1843       }
1844       else if (TickFormat->NBytes()>0) //no /TICKUNITS=> only 1 value taken into account
1845       {
1846         muaxdata.counter=0;
1847         muaxdata.nchars=0;
1848         muaxdata.what=GDL_TICKFORMAT;
1849         muaxdata.TickFormat=TickFormat;
1850         muaxdata.nTickFormat=1;
1851         a->slabelfunc( gdlMultiAxisTickFunc, &muaxdata );
1852         Opt+="o";
1853         if (modifierCode==2) Opt+="m"; else Opt+="n";
1854         if (axisId==XAXIS) a->box(Opt.c_str(), TickInterval, Minor, "", 0.0, 0);
1855         else if (axisId==YAXIS) a->box("", 0.0 ,0.0, Opt.c_str(), TickInterval, Minor);
1856         nchars=muaxdata.nchars;
1857         if (axisId==YAXIS) title_position=nchars+2; else title_position=3.5;
1858         a->slabelfunc( NULL, NULL );
1859       }
1860       else
1861       {
1862         tdata.nchars=0;
1863         a->slabelfunc( gdlSimpleAxisTickFunc, &tdata );
1864         Opt+="o";
1865         if (modifierCode==2) Opt+="m"; else Opt+="n";
1866         if (axisId==XAXIS) a->box(Opt.c_str(), TickInterval, Minor, "", 0.0, 0);
1867         else if (axisId==YAXIS) a->box("", 0.0 ,0.0, Opt.c_str(), TickInterval, Minor);
1868         nchars=tdata.nchars;
1869         if (axisId==YAXIS) title_position=nchars+2; else title_position=3.5;
1870         a->slabelfunc( NULL, NULL );
1871       }
1872 
1873       if (modifierCode==0 ||modifierCode==1)
1874       {
1875         if (axisId==XAXIS) a->mtex("b",title_position, 0.5, 0.5, Title.c_str());
1876         else if (axisId==YAXIS) a->mtex("l",title_position,0.5,0.5,Title.c_str());
1877       }
1878       else if (modifierCode==2)
1879       {
1880         if (axisId==XAXIS) a->mtex("t", title_position, 0.5, 0.5, Title.c_str());
1881         else if (axisId==YAXIS) a->mtex("r",title_position,0.5,0.5,Title.c_str());
1882       }
1883 
1884       if (TickLayout==0)
1885       {
1886         a->smaj(ticklen_in_mm, 1.0); //set base ticks to default 0.02 viewport converted to mm.
1887         a->smin(ticklen_in_mm/2.0,1.0); //idem min (plplt defaults)
1888         //thick for box and ticks.
1889         a->Thick(Thick);
1890 
1891         //ticks or grid eventually with style and length:
1892         if (abs(TickLen)<1e-6) Opt=""; else Opt="st"; //remove ticks if ticklen=0
1893         if (TickLen<0) {Opt+="i"; TickLen=-TickLen;}
1894         switch(modifierCode)
1895         {
1896           case 2:
1897             Opt+="c";
1898             break;
1899           case 1:
1900             Opt+="b";
1901             break;
1902           case 0:
1903             if ( (Style&8)==8 ) Opt+="b"; else Opt+="bc";
1904         }
1905         //gridstyle applies here:
1906         gdlLineStyle(a,GridStyle);
1907         if ( Log ) Opt+="l";
1908         if (axisId==XAXIS) a->box(Opt.c_str(), TickInterval, Minor, "", 0.0, 0);
1909         else if (axisId==YAXIS) a->box("", 0.0, 0, Opt.c_str(), TickInterval, Minor);
1910         //reset gridstyle
1911         gdlLineStyle(a,0);
1912         // pass over with outer box, with thick. No style applied, only ticks
1913         Opt=" ";
1914         switch(modifierCode)
1915         {
1916           case 2:
1917             Opt+="c";
1918             break;
1919           case 1:
1920             Opt+="b";
1921             break;
1922           case 0:
1923             if ( (Style&8)==8 ) Opt+="b"; else Opt+="bc";
1924         }
1925         if (axisId==XAXIS) a->box(Opt.c_str(), 0.0, 0, "", 0.0, 0);
1926         else if (axisId==YAXIS) a->box("", 0.0, 0 , Opt.c_str(), 0.0, 0);
1927       }
1928       //reset charsize & thick
1929       a->Thick(1.0);
1930       a->sizeChar(1.0);
1931     }
1932 	return 0;
1933   }
1934 
1935 
gdlBox(EnvT * e,GDLGStream * a,DDouble xStart,DDouble xEnd,DDouble yStart,DDouble yEnd,bool xLog,bool yLog)1936   static bool gdlBox(EnvT *e, GDLGStream *a, DDouble xStart, DDouble xEnd, DDouble yStart, DDouble yEnd, bool xLog, bool yLog)
1937   {
1938     gdlWriteTitleAndSubtitle(e, a);
1939     gdlAxis(e, a, XAXIS, xStart, xEnd, xLog);
1940     gdlAxis(e, a, YAXIS, yStart, yEnd, yLog);
1941     // title and sub title
1942     return true;
1943   }
gdlAxis3(EnvT * e,GDLGStream * a,int axisId,DDouble Start,DDouble End,bool Log,DLong zAxisCode=0,DDouble NormedLength=0)1944   static bool gdlAxis3(EnvT *e, GDLGStream *a, int axisId, DDouble Start, DDouble End, bool Log, DLong zAxisCode=0, DDouble NormedLength=0)
1945   {
1946     string addCode="b"; //for X and Y, and some Z
1947     if(zAxisCode==1 || zAxisCode==4) addCode="cm";
1948     bool doZ=(zAxisCode>=0);
1949 
1950     static GDL_TICKDATA tdata;
1951     tdata.a=a;
1952     tdata.isLog=Log;
1953     tdata.axisrange=abs(End-Start);
1954 
1955     static GDL_TICKNAMEDATA data;
1956     data.a=a;
1957     data.isLog=Log;
1958     data.axisrange=abs(End-Start);
1959 
1960     data.nTickName=0;
1961 
1962     static GDL_MULTIAXISTICKDATA muaxdata;
1963     muaxdata.a=a;
1964     muaxdata.isLog=Log;
1965     muaxdata.axisrange=abs(End-Start);
1966 
1967     muaxdata.what=GDL_NONE;
1968     muaxdata.nTickFormat=0;
1969     muaxdata.nTickUnits=0;
1970     muaxdata.axismin=Start;
1971     muaxdata.axismax=End;
1972     muaxdata.e=e;
1973     muaxdata.reset=true;
1974 
1975     //special values
1976     PLFLT OtherAxisSizeInMm;
1977     if (axisId==XAXIS) OtherAxisSizeInMm=a->mmyPageSize()*(a->boxnYSize());
1978     if (axisId==YAXIS) OtherAxisSizeInMm=a->mmxPageSize()*(a->boxnXSize());
1979     if (axisId==ZAXIS) OtherAxisSizeInMm=a->mmxPageSize()*(a->boxnXSize()); //not always correct
1980     //special for AXIS who change the requested box size!
1981     if (axisId==XAXIS2) {axisId=XAXIS; OtherAxisSizeInMm=a->mmyPageSize()*(NormedLength);}
1982     if (axisId==YAXIS2) {axisId=YAXIS; OtherAxisSizeInMm=a->mmxPageSize()*(NormedLength);}
1983     if (axisId==ZAXIS2) {axisId=ZAXIS; OtherAxisSizeInMm=a->mmxPageSize()*(NormedLength);} //not always correct
1984 
1985 //    DFloat Charsize;//done in gdlSetAxisCharsize() below
1986 //    gdlGetDesiredAxisCharsize(e, axisId, Charsize);
1987     DLong GridStyle;
1988     gdlGetDesiredAxisGridStyle(e, axisId, GridStyle);
1989 //    DFloat MarginL, MarginR; //unused yet (fixme)
1990 //    gdlGetDesiredAxisMargin(e, axisId, MarginL, MarginR);
1991     DLong Minor;
1992     gdlGetDesiredAxisMinor(e, axisId, Minor);
1993     DLong Style;
1994     gdlGetDesiredAxisStyle(e, axisId, Style);
1995     DFloat Thick;
1996     gdlGetDesiredAxisThick(e, axisId, Thick);
1997     DStringGDL* TickFormat;
1998     gdlGetDesiredAxisTickFormat(e, axisId, TickFormat);
1999     DDouble TickInterval;
2000     gdlGetDesiredAxisTickInterval(e, axisId, TickInterval);
2001     DLong TickLayout;
2002     gdlGetDesiredAxisTickLayout(e, axisId, TickLayout);
2003     DFloat TickLen;
2004     gdlGetDesiredAxisTickLen(e, axisId, TickLen);
2005     DStringGDL* TickName;
2006     gdlGetDesiredAxisTickName(e, a, axisId, TickName);
2007     DLong Ticks;
2008     gdlGetDesiredAxisTicks(e, axisId, Ticks);
2009     DStringGDL* TickUnits;
2010     gdlGetDesiredAxisTickUnits(e, axisId, TickUnits);
2011 //    DDoubleGDL* Tickv;
2012 //    gdlGetDesiredAxisTickv(e, axisId, Tickv);
2013     DString Title;
2014     gdlGetDesiredAxisTitle(e, axisId, Title);
2015 
2016     bool hasTickUnitDefined = (TickUnits->NBytes()>0);
2017     int tickUnitArraySize=(hasTickUnitDefined)?TickUnits->N_Elements():0;
2018     //For labels we need ticklen in current character size, for ticks we need it in mm
2019     DFloat ticklen_in_mm= TickLen;
2020     if (TickLen<0) ticklen_in_mm*=-1;
2021     //ticklen in a percentage of box x or y size, to be expressed in mm
2022     if (axisId==XAXIS) ticklen_in_mm=a->mmyPageSize()*(a->boxnYSize())*ticklen_in_mm;
2023     if (axisId==YAXIS) ticklen_in_mm=a->mmyPageSize()*(a->boxnXSize())*ticklen_in_mm;
2024     //eventually, each succesive X or Y axisId is separated from previous by interligne + ticklen in adequate units.
2025     DFloat interligne;
2026     if (axisId==XAXIS) interligne=a->mmLineSpacing()/a->mmCharHeight(); //in units of character size
2027     if (axisId==YAXIS) interligne=a->mmLineSpacing()/a->mmCharLength(); //in units of character size
2028 
2029     if ( (Style&4)!=4 ) //if we write the axis...
2030     {
2031       if (TickInterval==0)
2032       {
2033         if (Ticks<=0) TickInterval=gdlComputeTickInterval(e, axisId, Start, End, Log);
2034         else if (Ticks>1) TickInterval=(End-Start)/Ticks;
2035         else TickInterval=(End-Start);
2036       }
2037       //Following hopefully corrects a bug in plplot when TickInterval is very very tiny. The only solution
2038       // is to avoid plotting the axis!
2039       if (TickInterval < 10*std::numeric_limits<double>::epsilon()) {
2040        return 0;
2041       }
2042       string Opt;
2043       //first write labels only:
2044       gdlSetAxisCharsize(e, a, axisId);
2045       gdlSetPlotCharthick(e, a);
2046       // axis legend if box style, else do not draw. Take care writing BELOW/ABOVE all axis if tickunits present:actStream->wCharHeight()
2047       DDouble displacement=(tickUnitArraySize>1)?2.5*tickUnitArraySize:0;
2048 
2049       //no option to care of placement of Z axis???
2050       if (axisId==XAXIS) a->mtex3("xp",3.5+displacement, 0.5, 0.5, Title.c_str());
2051       else if (axisId==YAXIS) a->mtex3("yp",5.0+displacement,0.5,0.5,Title.c_str());
2052       else if (doZ) a->mtex3("zp",5.0+displacement,0.5,0.5,Title.c_str());
2053 
2054       //axis, 1st time: labels
2055       Opt=addCode+"nst"; //will write labels beside the left hand axis (u) at major ticks (n)
2056       if ( Log ) Opt+="l";
2057       if (TickName->NBytes()>0) // /TICKNAME=[array]
2058       {
2059         data.counter=0;
2060         data.TickName=TickName;
2061         data.nTickName=TickName->N_Elements();
2062         a->slabelfunc( gdlSingleAxisTickNamedFunc, &data );
2063         Opt+="o";
2064         if      (axisId==XAXIS) a->box3(Opt.c_str(), "" , TickInterval, Minor, "", "", 0.0, 0, "", "", 0.0, 0);
2065         else if (axisId==YAXIS) a->box3("", "", 0.0 ,0.0, Opt.c_str(),"", TickInterval, Minor, "", "", 0.0, 0);
2066         else if (doZ) if (axisId==ZAXIS) a->box3("", "", 0.0, 0, "", "", 0.0, 0, Opt.c_str(), "", TickInterval, Minor);
2067         a->slabelfunc( NULL, NULL );
2068       }
2069       //care Tickunits size is 10 if not defined because it is the size of !X.TICKUNITS.
2070       else if (hasTickUnitDefined) // /TICKUNITS=[several types of axes written below each other]
2071       {
2072         muaxdata.counter=0;
2073         muaxdata.what=GDL_TICKUNITS;
2074         if (TickFormat->NBytes()>0)  // with also TICKFORMAT option..
2075         {
2076           muaxdata.what=GDL_TICKFORMAT_AND_UNITS;
2077           muaxdata.TickFormat=TickFormat;
2078           muaxdata.nTickFormat=TickFormat->N_Elements();
2079         }
2080         muaxdata.TickUnits=TickUnits;
2081         muaxdata.nTickUnits=tickUnitArraySize;
2082         a->slabelfunc( gdlMultiAxisTickFunc, &muaxdata );
2083         Opt+="o";
2084         for (SizeT i=0; i< muaxdata.nTickUnits; ++i) //loop on TICKUNITS axis
2085         {
2086 // no equivalent in 3d yet...
2087 //          PLFLT un,deux,trois,quatre,xun,xdeux,xtrois,xquatre;
2088 //          a->plstream::gvpd(un,deux,trois,quatre);
2089 //          a->plstream::gvpw(xun,xdeux,xtrois,xquatre);
2090             if      (axisId==XAXIS) a->box3(Opt.c_str(), "", TickInterval, Minor, "", "", 0.0, 0, "", "", 0.0, 0);
2091             else if (axisId==YAXIS) a->box3("", "", 0.0 ,0.0, Opt.c_str(),"", TickInterval, Minor, "", "", 0.0, 0);
2092             else if (doZ) if (axisId==ZAXIS) a->box3("", "", 0.0, 0, "", "", 0.0, 0, Opt.c_str(), "", TickInterval, Minor);
2093 //          a->plstream::vpor(un,deux,trois,quatre);
2094 //          a->plstream::wind(xun,xdeux,xtrois,xquatre);
2095             muaxdata.counter++;
2096         }
2097         a->slabelfunc( NULL, NULL );
2098       }
2099       else if (TickFormat->NBytes()>0) //no /TICKUNITS=> only 1 value taken into account
2100       {
2101         muaxdata.counter=0;
2102         muaxdata.what=GDL_TICKFORMAT;
2103         muaxdata.TickFormat=TickFormat;
2104         muaxdata.nTickFormat=1;
2105         a->slabelfunc( gdlMultiAxisTickFunc, &muaxdata );
2106         Opt+="o";
2107         if      (axisId==XAXIS) a->box3(Opt.c_str(), "", TickInterval, Minor, "", "", 0.0, 0, "", "", 0.0, 0);
2108         else if (axisId==YAXIS) a->box3("", "", 0.0 ,0.0, Opt.c_str(),"", TickInterval, Minor, "", "", 0.0, 0);
2109         else if (doZ) if (axisId==ZAXIS) a->box3("", "", 0.0, 0, "", "", 0.0, 0, Opt.c_str(), "", TickInterval, Minor);
2110 
2111         a->slabelfunc( NULL, NULL );
2112       }
2113       else
2114       {
2115         a->slabelfunc( gdlSimpleAxisTickFunc, &tdata );
2116         Opt+="o";
2117         if      (axisId==XAXIS) a->box3(Opt.c_str(), "", TickInterval, Minor, "", "", 0.0, 0, "", "", 0.0, 0);
2118         else if (axisId==YAXIS) a->box3("", "", 0.0 ,0.0, Opt.c_str(),"", TickInterval, Minor, "", "", 0.0, 0);
2119         else if (doZ) if (axisId==ZAXIS) a->box3("", "", 0.0, 0, "", "", 0.0, 0, Opt.c_str(), "", TickInterval, Minor);
2120         a->slabelfunc( NULL, NULL );
2121       }
2122 
2123       if (TickLayout==0)
2124       {
2125         a->smaj(ticklen_in_mm, 1.0); //set base ticks to default 0.02 viewport converted to mm.
2126         a->smin(ticklen_in_mm/2.0,1.0); //idem min (plplt defaults)
2127         //thick for box and ticks.
2128         a->Thick(Thick);
2129 
2130         //ticks or grid eventually with style and length:
2131         if (abs(TickLen)<1e-6) Opt=""; else Opt="st"; //remove ticks if ticklen=0
2132         if (TickLen<0) {Opt+="i"; TickLen=-TickLen;}
2133 
2134         //gridstyle applies here:
2135         gdlLineStyle(a,GridStyle);
2136         if ( Log ) Opt+="l";
2137         if      (axisId==XAXIS) a->box3(Opt.c_str(), "", TickInterval, Minor, "", "", 0.0, 0, "", "", 0.0, 0);
2138         else if (axisId==YAXIS) a->box3("", "", 0.0 ,0.0, Opt.c_str(),"", TickInterval, Minor, "", "", 0.0, 0);
2139         else if (doZ) if (axisId==ZAXIS) a->box3("", "", 0.0, 0, "", "", 0.0, 0, Opt.c_str(), "", TickInterval, Minor);
2140         //reset ticks to default plplot value...
2141         //reset gridstyle
2142         gdlLineStyle(a,0);
2143         // pass over with outer box, with thick. No style applied, only ticks
2144         Opt="b";
2145         if      (axisId==XAXIS) a->box3(Opt.c_str(), "", TickInterval, Minor, "","",0,0,"","",0,0);
2146         else if (axisId==YAXIS) a->box3("","",0,0, Opt.c_str(), "", TickInterval, Minor, "","",0,0);
2147         else if (doZ) if (axisId==ZAXIS) a->box3("","",0,0,"","",0,0, Opt.c_str(), "", TickInterval, Minor);
2148       }
2149       //reset charsize & thick
2150       a->Thick(1.0);
2151       a->sizeChar(1.0);
2152     }
2153 	return 0;
2154   }
2155 
gdlBox3(EnvT * e,GDLGStream * a,DDouble xStart,DDouble xEnd,DDouble yStart,DDouble yEnd,DDouble zStart,DDouble zEnd,bool xLog,bool yLog,bool zLog,bool doSpecialZAxisPlacement=0)2156   static bool gdlBox3(EnvT *e, GDLGStream *a, DDouble xStart, DDouble xEnd, DDouble yStart,
2157       DDouble yEnd, DDouble zStart, DDouble zEnd, bool xLog, bool yLog, bool zLog, bool doSpecialZAxisPlacement=0)
2158   {
2159     DLong zAxisCode=0;
2160     static int ZAXISIx=e->KeywordIx("ZAXIS");
2161     if (doSpecialZAxisPlacement) e->AssureLongScalarKWIfPresent(ZAXISIx, zAxisCode);
2162     gdlAxis3(e, a, XAXIS, xStart, xEnd, xLog, 0);
2163     gdlAxis3(e, a, YAXIS, yStart, yEnd, yLog, 0);
2164     gdlAxis3(e, a, ZAXIS, zStart, zEnd, zLog, zAxisCode);
2165     // title and sub title
2166     gdlWriteTitleAndSubtitle(e, a);
2167     return true;
2168   }
2169 
2170 } // namespace
2171 
2172 #endif
2173