/* @(#)aglaxutl.c 17.1.1.1 (ES0-DMD) 01/25/02 17:33:29 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /* @(#)aglaxutl.c 17.1.1.1 (OAA-ASTRONET) 01/25/02 17:33:29 /* * HEADER : aglaxutl.c - Vers 3.6.006 - Nov 1993 - L. Fini, OAA * - Vers 3.6.005 - Sep 1993 - L. Fini, OAA * - Vers 3.6.004 - Jul 1993 - L. Fini, OAA * * Utilities for axes routines */ #include #include /*****************************************************************************/ /* AGLCleanFloat */ /* Gets an input string of the form 1.00 and returns the cleaned form */ /* without all those trailing zeroes */ static void AGLCleanFloat(input,output,cleanall) char *input; /* Input string +/-nnn.ddd */ char *output; /* Output string (AGL text format) */ int cleanall; /* if TRUE also clear decimal point */ { char *pt; pt = strchr(input,'\0')-1; while(pt>input) if(*pt == '0') pt--; else { if(*pt =='.') if(cleanall) pt--; else pt++; break; } while(input<=pt) *output++ = *input++; *output = '\0'; } /*****************************************************************************/ /* AGLCleanExp */ /* Gets an input string of the form 12.345e+678 and returns the exp form with*/ /* 10 to 678 using the AGL text metacharacter sequences. */ static void AGLCleanExp(input,output) char *input; /* Input string exponential format */ char *output; /* Output string (AGL text format) */ { char *src; src = strchr(input,'e'); *src++ ='\0'; AGLCleanFloat(input,output,FALSE); if(strcmp(output,"0.0") != 0) { strcat(output," 10~{~^"); if(*src == '-') strcat(output,"-");; src++; /* drop sign */ while(*src) if(*src == '0') src++; /* drop leading 0's */ else break; if(*src == '\0') src--; strcat(output,src); strcat(output,"~}"); } } /*****************************************************************************/ /* AG_round */ /* Roundoff to long integer */ static long AG_round(dval) double dval; /* Value to roundoff */ { int sign; sign = (dval<0.0)?(-1):(1); return (sign*floor(fabs((double)dval)+0.5)); } static void bestfmtd(axis) /* Return format spec */ struct AXIS *axis; { double qmax,qmin,divb,dfact,doffst; double ipmax,ipmin; dfact = axis->fact; doffst = axis->offst; divb = (axis->tikmj * dfact) + doffst; qmax = (axis->ltik * dfact) + doffst; qmin = (axis->ftik * dfact) + doffst; qmax = fabs(qmax); qmin = fabs(qmin); qmax = MAX(qmax,qmin); qmin = MIN(qmax,qmin); divb = fabs(divb); qmin = MIN(qmin,divb); qmin = (qmin==0.0)?divb:qmin; qmax *= 1.01; qmin *= 1.01; ipmax = floor(log10(qmax)); ipmin = floor(log10(qmin)); axis->iexp=0.0; if( (ipmax>3) || (ipmax<(-2)) ) { if(fabs((ipmax-ipmin))<=4.0) { axis->iexp=ipmin; strcpy(axis->form,"%5.2f"); } else { strcpy(axis->form,"%e"); } } else if(ipmin>=0) strcpy(axis->form,"%d"); else { int ndigits=fabs(ipmin); sprintf(axis->form,"%%.%df",ndigits); } } static double agltoint(val,sg) /* return closest integer, taking rounding */ /* errors into account */ double val; /* value to round up (alwais >=0) */ double sg; /* if <0 then return floor, else ceiling */ { double aux; double fr=val*1e-4; /* value to check for "close" integers */ if(sg<0) { aux=floor(val); if((aux+1.0-val)fr) aqmin += div; if((aqmax-qmax)>fr) aqmax -= div; *f = aqmin; *l = aqmax; } /*****************************************************************************/ /* AGLfmtd */ /* This module computes the best format and tik positions suited for decimal */ /* linear quotes along an axis. */ static void AGLfmtd(axis) struct AXIS *axis; /* Axis structure */ { double qmax,qmin,range,divb,divs; static double tresh[2] = { 1.7, 3.6 }; int i; double powdiv,decade; qmax = MAX(axis->divs[0],axis->divs[1]); qmin = MIN(axis->divs[0],axis->divs[1]); switch(axis->man) { case AUTO: range = (qmax-qmin); decade = floor(log10(range)); powdiv = exp(decade*LOG10); range = range / powdiv; /* Fact:: 1.0 <= range < 10.0 */ for(i=0;i<2;i++) if(rangetikmj = divb; axis->tikmn = divs * powdiv; break; case MANUAL: divb = axis->tikmj; } if((axis->man != NONE)&&(divb>0.0)) { frslst(qmin,qmax,divb, /* Compute first, last tik */ &(axis->ftik),&(axis->ltik)); if(*axis->form != '*') bestfmtd(axis); /* Return format spec */ } } /*****************************************************************************/ /* AGLfmta */ /* This module computes the best format and tik positions suited for angular */ /* linear quotes. */ static void AGLfmta(axis) struct AXIS *axis; /* Axis structure */ { double qmax,qmin,range,divb,divs; static double tresh[6] = { 1.1, 2.1, 3.1, 7.0, 13.0, 34.0 }; int ipmax,ipmin,i; double sexade; double powdiv; qmax = MAX(axis->divs[0],axis->divs[1]); qmin = MIN(axis->divs[0],axis->divs[1]); switch(axis->man) { case AUTO: range = (qmax-qmin); sexade = floor(log((double)range)/(double)LOG60); powdiv = exp((double)(sexade*LOG60)); range = range/powdiv; /* Fact:: 1 <= range < 60 */ for(i=0;i<6;i++) if(rangetikmj = divb; axis->tikmn = divs * powdiv; break; case MANUAL: divb = axis->tikmj; } if(axis->man!=NONE) { frslst(qmin,qmax,divb, /* Compute first and last tik */ &(axis->ftik),&(axis->ltik)); /* Now find out the best format */ qmax = (axis->ltik * axis->fact) + axis->offst; qmin = (axis->ftik * axis->fact) + axis->offst; qmax = fabs((double)qmax); qmin = fabs((double)qmin); qmax = MAX(qmax,qmin); qmax *= 1.01; divb *= 1.01; ipmax = floor(log((double)qmax)/(double)LOG60); ipmin = floor(log((double)divb)/(double)LOG60); if(*axis->form != '*') { if(ipmin>=0) { bestfmtd(axis); return; } if(ipmax>=0) { if(ipmin==(-1)) { strcpy(axis->form,"%am"); return; } if(ipmin==(-2)) { strcpy(axis->form,"%as"); return; } *axis->form = '\0'; return; } if(ipmax>(-2)) { strcpy(axis->form,"%ms"); return; } strcpy(axis->form,"%ss"); } } } /*****************************************************************************/ /* AGLfmtl */ /* This module computes the best format and tik positions suited for log */ /* quotes. */ static void AGLfmtl(axis) struct AXIS *axis; /* Axis structure */ { double qmax,qmin,divb,divs; double q0,q1; int ndecs,i; static int dectrsh[7] = { 6,10,15,23,47,63,95 }; q0 = axis->divs[0]*axis->fact + axis->offst; q1 = axis->divs[1]*axis->fact + axis->offst; qmax = MAX(q0,q1); qmax = log10((double)qmax); qmin = MIN(q0,q1); qmin = log10((double)qmin); switch(axis->man) { case AUTO: ndecs = qmax-qmin+1; /* get optim. divisions */ for(i=0;i<6;i++) if(ndecstikmn = divs; axis->tikmj = exp((double)(divb*LOG10)); break; case MANUAL: divb = floor(log10((double)axis->tikmj)); divb = (divb<=0) ? 1 : divb; break; } if(axis->man != NONE) { frslst(qmin,qmax,divb, /* Compute first, last tik */ &(axis->ftik),&(axis->ltik)); axis->ftik = exp((double)(axis->ftik * LOG10)); axis->ltik = exp((double)(axis->ltik * LOG10)); if(*axis->form != '*') strcpy(axis->form,"%p"); } } /*****************************************************************************/ /* AGLfmtp */ /* This module computes the best format and tik positions suited for quotes */ /* expressed as multiples of PI */ static void AGLfmtp(axis) struct AXIS *axis; { float aux,n; float qmax,qmin; float angfact; angfact = AGLAngF(axis->Angmode); axis->tikmj=30*angfact; /* PI/6 (30 degrees) */ axis->tikmn=15*angfact; /* PI/12 (15 degrees) */ qmin=MIN(axis->divs[0],axis->divs[1]); qmax=MAX(axis->divs[0],axis->divs[1]); aux = qmin/axis->tikmj; /* first tik */ n = floor(aux); n = ((aux-n)<=1.e-4)?n:(n+1.0); axis->ftik = n * axis->tikmj; axis->ltik = floor(qmax/axis->tikmj+1.e-5) * /* last tik */ axis->tikmj; if(*axis->form != '*') if(axis->Angmode==RADIANS) strcpy(axis->form,"%5.2f"); else strcpy(axis->form,"%d"); } /*****************************************************************************/ /* AGLfmt */ /* This module computes the best format and tik positions suited for the */ /* axis mode of quotation */ void AGLfmt(axis) struct AXIS *axis; /* Axis structure */ { switch(axis->mode) { case LIN: AGLfmtd(axis); break; case ANG: switch(axis->Angmode) { case RADIANS: AGLfmtp(axis); break; case HOURS: AGLfmta(axis); break; case DEGREES: AGLfmta(axis); break; } break; case LOG: AGLfmtl(axis); break; } } /*****************************************************************************/ /* AGLAngF */ /* This module computes the factor to convert a given angular representation */ /* into radians */ double AGLAngF(angmode) enum ANGLES angmode; { switch(angmode) { default: case RADIANS: return 1.0; case DEGREES: return .017453292519943295769236907684; case HOURS: return .261799387799149436538553615273; } } /*****************************************************************************/ /* AGLSex */ /* Convert a value into sexagesimal format */ #define MODF(i,v,base) v*=(base); i=(int)(v); v-=(i) static void AGLSex(ostr,form,value) char *ostr; /* output string (it must be long enough) */ char *form; /* format specifier */ /* %au (S) : Degrees */ /* %aum (S) : Degrees minutes */ /* %aums (S) : Degrees minutes seconds */ /* %ams (S) : minutes seconds */ /* %amss (S) : minutes seconds fraction */ /* %hu (S) : Hours */ /* %hum (S) : Hours minutes */ /* %hums (S) : Hours minutes seconds */ /* %hms (S) : minutes seconds */ /* %hmss (S) : minutes seconds fraction */ /* Add more s' to the end to get greather */ /* precision */ double value; /* value to convert */ { static char *sep_a[3] = { "~{~^o~}~< ", "'" , "\"" }; static char *sep_h[3] = { "~{~^h~}~< " , "~{~^m~}~< " , "~{~^s~}~< " }; static char *frm0 = "%d%s"; static char *frm1 = "%2.2d%s"; static char *stars= "***"; char *frm = frm0; char **sep; char *pt; int ifield[10]; int nfield,first=(-1),last; int curl; strcpy(ostr,stars); if(value<0.0) { *ostr = '-'; pt = ostr+1; value=fabs(value); } else pt = ostr; if(*form != '%') return; form++; switch(TOLOWER(*form)) { /* Select format of separator */ case 'h': sep = sep_h; break; case 'a': sep = sep_a; break; default: return; } form++; for(nfield=0;nfield<10;nfield++) ifield[nfield]=0; if(TOLOWER(*form)=='u') { /* put units into string */ if(first<0) first=0; last=0; MODF(ifield[0],value,1.0); form++; } if(TOLOWER(*form)=='m') { /* put minutes into string */ if(first<0) first=1; last=1; MODF(ifield[1],value,60.0); form++; } if(TOLOWER(*form)=='s') { /* put seconds into string */ if(first<0) first=2; last=2; MODF(ifield[2],value,60.0); form++; } if(first<0) return; while(last>1 && last<9 && *form) { /* put fractions */ last++; MODF(ifield[last],value,10.0); form++; } if(value>=0.5) /* Roundoff */ ifield[last]++; for(nfield=last; nfield>first; nfield--) /* Propagate */ if(nfield>2) { if(ifield[nfield]>9) { ifield[nfield] -= 10; ifield[nfield-1]++; } } else { if(ifield[nfield]>59) { ifield[nfield] -= 60; ifield[nfield-1]++; } } if(first>0 && (ifield[first-1]>0)) { /* Overflow */ strcpy(ostr,stars); return; /* Return *** */ } frm = frm0; curl = (last>2) ? 2 : last; for(nfield=first; nfield<=curl; nfield++) { /* make the string */ sprintf(pt,frm,ifield[nfield],sep[nfield]); pt=strchr(pt,'\0'); frm = frm1; } if(last>2) { strcat(ostr,"~<."); pt=strchr(pt,'\0'); for(nfield=3; nfield<=last; nfield++) { /* make the string */ sprintf(pt,"%d",ifield[nfield]); pt=strchr(pt,'\0'); } } } /*****************************************************************************/ /* AGLExp */ static void AGLExp(sel,value,string) int sel; /* either 'p' for power of ten or 'x' for */ /* power of e */ double value; /* value to convert */ char *string; /* output string */ { int intval; if(sel=='p') { value = log10(value); *string++ = '1'; *string++ = '0'; } else { value = log(value); *string++ = 'e'; } intval=AG_round(value); sprintf(string,"~{~^%d~}",intval); } void AGLConvert(form,value,chstrg) /* Convert a number into proper */ /* representation */ char *form; double value; char *chstrg; { char aux[20]; char lform[10]; char *src, *dst; int to_clean; int free=TRUE; strcpy(chstrg,"****"); src=form; /* Move precision fields into local string */ dst=lform; *dst++ = *src++; while(*src) { if(isalpha(*src)) break; *dst++ = *src++; free=FALSE; } *dst='\0'; to_clean=(dst==(lform+1)); switch(*src) { /* src should point to format specifier */ int ndigs; case 'd': case 'D': strcat(lform,"d"); sprintf(chstrg,lform,(int)AG_round(value)); break; case 'e': case 'E': if(free) strcpy(lform,"%.4e"); else strcat(lform,"e"); sprintf(aux,lform,value); AGLCleanExp(aux,chstrg); break; case 'g': case 'G': if(value!=0.0) ndigs=log10(fabs(value))+1; else ndigs=1; if((ndigs<(-5))||(ndigs>5)) { strcpy(lform,"%.4e"); sprintf(aux,lform,value); AGLCleanExp(aux,chstrg); } else { ndigs = -(ndigs-5); ndigs = (ndigs<1) ? 1 : ndigs; sprintf(lform,"%%.%df",ndigs); sprintf(chstrg,lform,value); AGLCleanFloat(chstrg,chstrg,TRUE); } break; case 'f': case 'F': default: if(free) { if(value!=0.0) ndigs=log10(fabs(value))+1; else ndigs=1; ndigs = -(ndigs-5); ndigs = (ndigs<1) ? 1 : ndigs; sprintf(lform,"%%.%df",ndigs); sprintf(chstrg,lform,value); } else strcat(lform,"f"); sprintf(chstrg,lform,value); if(to_clean) AGLCleanFloat(chstrg,chstrg,FALSE); break; case 'p': case 'P': AGLExp('p',value,chstrg); break; case 'x': case 'X': AGLExp('x',value,chstrg); break; case 'a': case 'A': case 'h': case 'H': case 'm': case 'M': case 's': case 'S': strcat(lform,src); AGLSex(chstrg,lform,value); break; } } void AGLsset(cmd,mmode) /* Set mode with metafile control */ char *cmd; int mmode; { if(mmode==2) AG_MSUS(); AG_SSET(cmd); if(mmode==2) AG_MRES(); } double AGLnmjt(ctik,logflg,step) /* Get nex major tik position */ double ctik; /* Current tik position */ int logflg; /* Axis mode */ double step; /* Tik distance */ { if(logflg) return (ctik * step); else return (ctik + step); } #define GUARD_VALUE 0.0005 double AGLGuardL(stop,logflg,step) /* Provides a value which is suitably*/ /* smaller then stop to check for */ /* last tick taking rounding errors */ /* into account */ double stop; int logflg; double step; { if(logflg) return stop/pow(step,GUARD_VALUE); else return stop-step*GUARD_VALUE; } double AGLGuardU(stop,logflg,step) /* Provides a value which is suitably*/ /* greather then stop to check for */ /* last tick taking rounding errors */ /* into account */ double stop; int logflg; double step; { if(logflg) return stop*pow(step,GUARD_VALUE); else return stop+step*GUARD_VALUE; } double AGLnmnt(ctik,logflg,divs,mjtk) /* Get nex minor tik position */ double ctik; /* Current tik position */ int logflg; /* Axis mode */ double divs; /* Tik distance (<0: backwards) */ double mjtk; /* Major division distance */ { if(logflg) { if(divs>0.0) { if((divs>0.1)&&(divs<10.0)) return (ctik+(divs*mjtk)); else return (ctik*divs); } else { divs=fabs(divs); if((divs>0.1)&&(divs<10.0)) return (ctik-(divs*(mjtk*0.1))); else return (ctik/divs); } } else return (ctik+divs); }