/*===========================================================================
  Copyright (C) 1995-2009 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
===========================================================================*/

/*
 * HEADER : ag_nlin.c      - Vers 3.6.004  - Sep 1993 -  L. Fini, OAA
 *                         - Vers 3.6.003  - Jul 1993 -  L. Fini, OAA
 *                         - Vers 3.6.002  - Jul 1992 -  L. Fini, OAA
 *
 * AGL support for non linear coordinates 

 .VERSION

 090630		last modif
 */

#include <aglnlin.h>
#include <math.h>

#define BOUNDARY1(c,min,max) (((c)<(min)) ? 1 : ((c)>(max)) ? 2 : 0)
#define BOUNDARY2(c,min,max) (((c)<(min)) ? 4 : ((c)>(max)) ? 8 : 0)

static char *INT = "%d";
static char *FLOAT = "%5.2f";
static char *DDMM = "%2a";
static char *HHMM = "%2h";

static double c1fact,c2fact;		/* Coordinate conversion factors    */
static double c1ofs,c2ofs;		/* used by all the projections      */
static double c1_min,c1_max,c2_min,c2_max;

static char *Range = "Range";


/***************************************************************************/
/*                             AGLatanh                                   */

static double AGLatanh(x)	/* Computes the inverse of the hyperbolic  */
				/* tangent                                 */
double x;
{
return(0.5*log((1.0+x)/(1.0-x)));
}



/***************************************************************************/
static void init_gen(pl_stat)		/* Initialize status (general)      */
struct NLN_STATUS *pl_stat;
{
pl_stat->Proj = (-1);

pl_stat->c1.man   = AUTO;	pl_stat->c2.man   = AUTO;
pl_stat->c1.grid  = TRUE;	pl_stat->c2.grid  = TRUE;
pl_stat->c1.mode  = AX_UNDEF;	pl_stat->c2.mode  = AX_UNDEF;

pl_stat->Bold=0;

pl_stat->Fixit=FALSE;
pl_stat->Windfix=FALSE;

pl_stat->Txtdim = -11.0;

pl_stat->c1.label[0] = '\0'; 
pl_stat->c2.label[0] = '\0';
pl_stat->Rlabel[0] = '\0';
pl_stat->Title[0] = '\0';

*pl_stat->c1.form = '\0';      
*pl_stat->c2.form = '\0';

pl_stat->c1.Angmode = DEGREES;
pl_stat->c2.Angmode = DEGREES;

pl_stat->c1.fact = 1.0;  
pl_stat->c2.fact = 1.0;
pl_stat->c1.offst = 0.0; 
pl_stat->c2.offst = 0.0;
pl_stat->c1.iexp = 0.0;
pl_stat->c2.iexp = 0.0;
AG_IGET("mfode",&(pl_stat->Metamode));
}


/*****************************************************************************/
/*                               MSSET                                       */

static void MSSET(cmd,mmode)
char *cmd;
int mmode;
{
if(mmode==2)
	AG_MSUS();
AG_SSET(cmd);

if(mmode==2)
	AG_MRES();
}


static int lerange(axis,span) 		/* Check axis range <= span         */
struct AXIS *axis;
double span;
{
double angfct;
double w0,w1;

angfct = AGLAngF(axis->Angmode);

w0=axis->wind[0]*angfct;
w1=axis->wind[1]*angfct;

if(w1-w0 > span) {
		AG_GERR(201,Range);
		return FAILURE;
}
return SUCCESS;
}



static int ltrange(axis,span) 		/* Check axis range < span          */
struct AXIS *axis;
double span;
{
double angfct;
double w0,w1;

angfct = AGLAngF(axis->Angmode);

w0=axis->wind[0]*angfct;
w1=axis->wind[1]*angfct;

if(w1-w0 >= span) {
		AG_GERR(201,Range);
		return FAILURE;
}
return SUCCESS;
}



/****************************************************************************/
/*                            Polar routines                                */

static void pol_init()
{
float fact[6];

(void)AG_RGET("user",fact);	/* retrieve transformation parameters       */

c1fact=fact[0];
c2fact=fact[1];
c1_min=fact[2];
c1_max=fact[3];
c2_min=fact[4];
c2_max=fact[5];
}


static int pol_direct(rho,theta)	/* Convert a (rho,theta) value into */
					/* (x,y)                            */
double *rho, *theta;
{
double c0,c1;
int retval=0;

c0= *rho;
c1= *theta;

retval |= BOUNDARY1(c0,c1_min,c1_max);
retval |= BOUNDARY2(c1,c2_min,c2_max);

c0=c0*c1fact;
c1=c1*c2fact;

*rho=c0*cos(c1);
*theta=c0*sin(c1);

return retval;
}

static int pol_reverse(xx,vyy)		/* Convert a (x,y) value into       */
					/* (rho,theta)                      */
double *xx, *vyy;
{
double c0,c1,c2,c3;
int retval=0;

c2 = *xx;
c3 = *vyy;

c0 = hypot(c3,c2)/c1fact;
c1 = atan2(c3,c2)/c2fact;

retval |= BOUNDARY1(c0,c1_min,c1_max);
retval |= BOUNDARY2(c1,c2_min,c2_max);

*xx = c0;
*vyy = c1;

return retval;
}


/****************************************************************************/
/*                            Gnomonic routines                             */

static void gno_init()		/* Also used by other projections ....      */
{
float fact[8];

(void)AG_RGET("user",fact);	/* retrieve transformation parameters       */
c1ofs = fact[0];
c1_min= fact[1];
c1_max= fact[2];
c1fact= fact[3];
c2ofs = fact[4];
c2_min= fact[5];
c2_max= fact[6];
c2fact= fact[7];
}



static int gno_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double ang;
register double cos1,sin1,cos2,sin2;
int retval = 0;

cos1 = *c1;
cos2 = *c2;

retval |= BOUNDARY1(cos1,c1_min,c1_max);
retval |= BOUNDARY2(cos2,c2_min,c2_max);

ang = (cos1-c1ofs)*c1fact;	/* Center transformation latitude           */
cos1=cos(ang);
sin1=sin(ang);

ang = c2fact*(cos2-c2ofs);	/* Center transformation longitude          */
cos2=cos(ang);
sin2=sin(ang);

ang = 1.0/(cos2*cos1);

*c1 = (cos2*sin1)*ang;
*c2 = sin2*ang;

return retval;
}


static int gno_reverse(c1,c2)	/* Reverse gnomonic conversion               */
double *c1, *c2;
{
int retval=0;
register double X= *c1,Y= *c2;

X=atan(X);
Y=atan(Y*cos(X));

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_gnomo(ax)	/* Check gnomonic mode                      */
struct NLN_STATUS *ax;
{
ax->c1.mode = ANG;
ax->c2.mode = LIN;

if(*ax->c1.form!='*')
	strcpy(ax->c1.form,FLOAT);

if(*ax->c2.form!='*')
	strcpy(ax->c2.form,FLOAT);
return SUCCESS;
}

static int make_gnomo(ax)	/* Set up the gnomonic transformation       */
struct NLN_STATUS *ax;
{
float rfact[8];
double ac1,ac2;

rfact[0] = (ax->c1.wind[1]+			/* Center of first axis     */
            ax->c1.wind[0])*0.5;	
rfact[1] = ax->c1.wind[0];			/* limits of first axis     */
rfact[2] = ax->c1.wind[1];
rfact[3] = AGLAngF(ax->c1.Angmode);	/* Angular factor of first axis     */

rfact[4] = (ax->c2.wind[1]+		/* center of second axis            */
            ax->c2.wind[0])*0.5;
rfact[5] = ax->c2.wind[0];			/* limits of second axis    */
rfact[6] = ax->c2.wind[1];
rfact[7] = AGLAngF(ax->c2.Angmode);	/* Angular factor of second axis    */

AG_RSET(8,rfact);

AG_TRNS(gno_init,		/* Set up user defined transformation       */
        gno_direct,
        gno_reverse);
gno_init();
					/* Compute cartesian window          */
					/* and transformation center         */
ac1=ax->c1.wind[0];
ac2=ax->c2.wind[0];
gno_direct(&ac1,&ac2);
ax->Wind[0]=ac1;
ax->Wind[2]=ac2;

ac1=ax->c1.wind[1];
ac2=ax->c2.wind[1];
gno_direct(&ac1,&ac2);
ax->Wind[1]=ac1;
ax->Wind[3]=ac2;

return SUCCESS;
}



/****************************************************************************/
/*                             make_polar                                   */

static int make_polar(ax) 	/* Set up the polar transformation          */
struct NLN_STATUS *ax;
{
float rfact[6];
double angfct;

rfact[0]=1.0/ax->c1.wind[1];
angfct=AGLAngF(ax->c2.Angmode);
rfact[1]=angfct;

rfact[2] = ax->c1.wind[0];
rfact[3] = ax->c1.wind[1];
rfact[4] = ax->c2.wind[0];
rfact[5] = ax->c2.wind[1];

AG_RSET(6,rfact);		/* Store data in User storage area          */

AG_TRNS(pol_init,		/* Set up user defined transformation       */
        pol_direct,
        pol_reverse);

if(!ax->Windfix) {
	double t0=ax->c2.wind[0];
	double t1=ax->c2.wind[1];
	double minx=0,maxx=0,miny=0,maxy=0;

	t0 *= angfct ;
	t1 *= angfct ;

	if((t0<=0.0)&&(t1>=0.0)) 
		maxx=1.0;

	if((t0<=AG_PI05)&&(t1>=AG_PI05))
		maxy=1.0;

	if((t0<=AG_PI)&&(t1>=AG_PI))
		minx=(-1.0);

	if((t0<=AG_PI15)&&(t1>=AG_PI15))
		miny=(-1.0);

	if(minx==0.0) {
		double c0=cos(t0);
		double c1=cos(t1);
		minx=MIN3(minx,c0,c1);
	}
	if(maxx==0.0) {
		double c0=cos(t0);
		double c1=cos(t1);
		maxx=MAX3(maxx,c0,c1);
	}
	if(miny==0.0) {
		double s0=sin(t0);
		double s1=sin(t1);
		miny=MIN3(miny,s0,s1);
	}
	if(maxy==0.0) {
		double s0=sin(t0);
		double s1=sin(t1);
		maxy=MAX3(maxy,s0,s1);
	}
	ax->Wind[0]=minx;
	ax->Wind[1]=maxx;
	ax->Wind[2]=miny;
	ax->Wind[3]=maxy;
}

if(ax->c1.mode==LOG) 
	MSSET("logx",ax->Metamode); 
else 
	MSSET("linx",ax->Metamode);

ax->c2.ftik= rfact[4];
ax->c2.ltik= rfact[5];
ax->c2.tikmj= 0.52359878/angfct;

if(ax->c1.ftik<(0.5*ax->c1.tikmj))
	ax->c1.ftik += ax->c1.tikmj;

MSSET("geom",ax->Metamode);
return SUCCESS;
}

static void polar_quote(ax)		/* Quotes Polar c2 axis             */
struct NLN_STATUS *ax;
{
double th,thmax;
double rh;
double angf=AGLAngF(ax->c2.Angmode);

th=ax->c2.wind[0];
thmax=ax->c2.wind[1]*1.001;
rh=ax->c1.wind[1];

MSSET("lfrg",ax->Metamode);

while(th<=thmax) {
	double quote;
	double ct,st;
	char string[60];
	int qct;

	quote = th;
	ct=cos(angf*quote);
	st=sin(angf*quote);

	if(ct<0.9961947) {
		if(ct<-0.9961947)
			qct=16;
		else if(st>0.9961947)
			qct=12;
		else if(st<-0.9961947)
			qct=20;
		else if(ct>0.0)
			qct = (st>0.0) ? 9 : 23;
		else
			qct = (st>0.0) ? 15 : 17;

		quote = quote*ax->c2.fact+ax->c2.offst;

		AGLConvert(ax->c2.form,quote,string);
		AG_GTXT(rh,th,string,qct);
	}
	th += ax->c2.tikmj;
}
}


/****************************************************************************/
/*                              Aitoff routines                             */

static void ait_init()
{
float fact[8];

(void)AG_RGET("user",fact);	/* retrieve transformation parameters       */
c1ofs = fact[0];
c1_min= fact[1];
c1_max= fact[2];
c1fact= fact[3]*0.5;
c2ofs = fact[4];
c2_min= fact[5];
c2_max= fact[6];
c2fact= fact[7];
}


static int ait_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double ang;
register double cos1,sin1,cos2,sin2;
int retval = 0;

cos1 = *c1;
cos2 = *c2;

retval |= BOUNDARY1(cos1,c1_min,c1_max);
retval |= BOUNDARY2(cos2,c2_min,c2_max);

ang = (cos1-c1ofs)*c1fact;	/* Center transformation latitude           */
cos1=cos(ang);
sin1=sin(ang);

ang = c2fact*(cos2-c2ofs);	/* Center transformation longitude          */
cos2=cos(ang);
sin2=sin(ang);

ang = 1.0/sqrt((1.0+cos2*cos1)*0.5);

*c1 = 2.0*(cos2*sin1)*ang;
*c2 = sin2*ang;

return retval;
}


static int ait_reverse(c1,c2)	/* Reverse aitoff conversion                */
double *c1, *c2;		/* Longitude,latitude                       */
{
int retval=0;
register double X= *c1,Y= *c2;
double D,X2,Y2,D2;

X2 = X*X; 
Y2 = Y*Y;

D2 = 1.0 - (X2*0.0625) - (Y2*0.25);

if(D2<0.0)
	return CRSUNDEF;

D = sqrt(D2);
X = atan2((X*D*(2.0*D2-1.0)),(1.0-D2*(X2*0.5+Y2)));
Y = asin(Y*D);

X = 0.5*X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_aitoff(ax)	/* Initialize aitoff mode                   */
struct NLN_STATUS *ax;
{

ax->c1.mode = ANG;
ax->c2.mode = ANG;

if(*ax->c1.form!='*')
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*')
	strcpy(ax->c2.form,INT);

return SUCCESS;
}



/****************************************************************************/
/*                              GLS routines                                */


static int gls_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double L,B;
int retval = 0;

L = *c1;
B = *c2;

retval |= BOUNDARY1(L,c1_min,c1_max);
retval |= BOUNDARY2(B,c2_min,c2_max);

L = (L-c1ofs)*c1fact;		/* Center transformation latitude           */

B = c2fact*(B-c2ofs);		/* Center transformation longitude          */

*c2 = B;
*c1 = L*cos(B);

return retval;
}


static int gls_reverse(c1,c2)	/* Reverse gls    conversion                */
double *c1, *c2;		/* Longitude,latitude                       */
{
int retval=0;
register double X= *c1,Y= *c2;

X = X/cos(Y);

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_gls(ax)		/* Initialize gls    mode                   */
struct NLN_STATUS *ax;
{

ax->c1.mode = LIN;
ax->c2.mode = ANG;

if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);

return SUCCESS;
}




/****************************************************************************/
/*                             SIN2 routines                                */



static int s2_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double ang;
register double cos1,sin1,cos2,sin2;
int retval = 0;

cos1 = *c1;
cos2 = *c2;

retval |= BOUNDARY1(cos1,c1_min,c1_max);
retval |= BOUNDARY2(cos2,c2_min,c2_max);

ang = c1fact*(cos1-c1ofs);	/* Center transformation latitude           */
cos1 = cos(ang);
sin1 = sin(ang);

ang = c2fact*(cos2-c2ofs);	/* Center transformation longitude          */
cos2 = cos(ang);
sin2 = sin(ang);

ang = sqrt(2.0/(1.0+cos1*cos2));

*c1 = sin1*cos2*ang;
*c2 = sin2*ang;

return retval;
}


static int s2_reverse(c1,c2)	/* Reverse gls    conversion                */
double *c1, *c2;		/* Longitude,latitude                       */
{
int retval=0;
register double X= *c1,Y= *c2;
register double rho2,D;

rho2 = X*X+Y*Y;
D    = sqrt(1.0-0.25*rho2);

X = atan2((X*D),(1.0-0.5*rho2));

Y = asin(Y*D);

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_s2(ax)		/* Initialize gls    mode                   */
struct NLN_STATUS *ax;
{

ax->c1.mode = ANG;
ax->c2.mode = ANG;

if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);

return SUCCESS;
}



/****************************************************************************/
/*                         mercator routines                                */


static int mrc_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double X,Y;
int retval = 0;

X = *c1;
Y = *c2;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

X = c1fact*(X-c1ofs);	/* Center transformation latitude           */
Y = c2fact*(Y-c2ofs);	/* Center transformation longitude          */

*c1 = X;
*c2 = AGLatanh(sin(Y));

return retval;
}


static int mrc_reverse(c1,c2)	/* Reverse gls    conversion                */
double *c1, *c2;		/* Longitude,latitude                       */
{
int retval=0;
register double X= *c1,Y= *c2;

Y = asin(tanh(Y));

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_mrc(ax)		/* Initialize gls    mode                   */
struct NLN_STATUS *ax;
{

ax->c1.mode = LIN;
ax->c2.mode = LIN;

if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);

return SUCCESS;
}


/****************************************************************************/
/*                            ARC      routines                             */



static int arc_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double vyy,ang;
register double cos1,sin1,cos2,sin2;
int retval = 0;

cos1 = *c1;
cos2 = *c2;

retval |= BOUNDARY1(cos1,c1_min,c1_max);
retval |= BOUNDARY2(cos2,c2_min,c2_max);

ang = c1fact*(cos1-c1ofs);	/* Center transformation latitude           */
cos1=cos(ang);
sin1=sin(ang);

ang = c2fact*(cos2-c2ofs);	/* Center transformation longitude          */
cos2=cos(ang);
sin2=sin(ang);

vyy = sin1*cos2;

ang=hypot(vyy,sin2);

if(ang!=0)
	ang=acos(cos1*cos2)/ang;

*c1 = vyy*ang;
*c2 = sin2*ang;

return retval;
}


static int arc_reverse(c1,c2)	/* Reverse arcmonic conversion               */
double *c1, *c2;
{
int retval=0;
register double X= *c1,Y= *c2;
register double D,D2,sinc;

D2 = X*X+Y*Y;
D = sqrt(D2);

sinc = (D==0.0) ? 1.0 : sin(D)/D;

X=atan2(X*sinc,cos(D2));

Y=asin(Y*sinc);

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_arc(ax)		/* Check arc transformation                 */
struct NLN_STATUS *ax;
{
ax->c1.mode = ANG;
ax->c2.mode = ANG;

if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);
return SUCCESS;
}


static int make_arc(ax)	/* Set up the gnomonic transformation       */
struct NLN_STATUS *ax;
{
float rfact[8];
double ac1,ac2;

rfact[0] = (ax->c1.wind[1]+			/* Center of first axis     */
            ax->c1.wind[0])*0.5;
rfact[1] = ax->c1.wind[0];			/* limits of first axis     */
rfact[2] = ax->c1.wind[1];
rfact[3] = AGLAngF(ax->c1.Angmode);	/* Angular factor of first axis     */

rfact[4] = (ax->c2.wind[1]+			/* Center of second axis    */
            ax->c2.wind[0])*0.5;
rfact[5] = ax->c2.wind[0];			/* limits of second axis    */
rfact[6] = ax->c2.wind[1];
rfact[7] = AGLAngF(ax->c2.Angmode);	/* Angular factor of second axis    */

AG_RSET(8,rfact);

AG_TRNS(gno_init,		/* Set up user defined transformation       */
        arc_direct,
        arc_reverse);
gno_init();
					/* Compute cartesian window          */
					/* and transformation center         */
ac1=ax->c1.wind[0];
ac2=0.0;
arc_direct(&ac1,&ac2);
ax->Wind[0]=ac1;
ax->Wind[1]= -ac1;

ac1=ax->c1.wind[1];
ac2=ax->c2.wind[1];
arc_direct(&ac1,&ac2);
ax->Wind[2]= -ac2;
ax->Wind[3]= ac2;

MSSET("geom",ax->Metamode);

return SUCCESS;
}


/****************************************************************************/
/*                            TAN2     routines  Stereographics             */

static void t2_init()
{
float fact[8];

(void)AG_RGET("user",fact);	/* retrieve transformation parameters       */
c1ofs = fact[0];
c1_min= fact[1];
c1_max= fact[2];
c1fact= fact[3];
c2ofs = fact[4];
c2_min= fact[5];
c2_max= fact[6];
c2fact= fact[7];
}


static int t2_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double ang;
register double cos1,sin1,cos2,sin2;
int retval = 0;

cos1 = *c1;
cos2 = *c2;

retval |= BOUNDARY1(cos1,c1_min,c1_max);
retval |= BOUNDARY2(cos2,c2_min,c2_max);

ang = c1fact*(cos1-c1ofs);	/* Center transformation latitude           */
cos1=cos(ang);
sin1=sin(ang);

ang = c2fact*(cos2-c2ofs);	/* Center transformation longitude          */
cos2=cos(ang);
sin2=sin(ang);

ang = 2.0/(1.0+cos1*cos2);

*c1 = sin1*cos2*ang;
*c2 = sin2*ang;

return retval;
}


static int t2_reverse(c1,c2)	/* Reverse arcmonic conversion               */
double *c1, *c2;
{
int retval=0;
register double X= *c1,Y= *c2;
register double R2;

R2 = (X*X+Y*Y)*0.25;

X=atan2(X,(1.0-R2));

Y=asin(Y/(1.0+R2));

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_t2(ax)		/* Check t2 transformation                 */
struct NLN_STATUS *ax;
{
ax->c1.mode = ANG;
ax->c2.mode = ANG;

if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);
return SUCCESS;
}


static int make_t2(ax)		/* Set up the stereographic transformation */
struct NLN_STATUS *ax;
{
float rfact[8];
double ac1,ac2;

rfact[0] = (ax->c1.wind[1]+			/* Center of first axis     */
            ax->c1.wind[0])*0.5;
rfact[1] = ax->c1.wind[0];			/* limits of first axis     */
rfact[2] = ax->c1.wind[1];
rfact[3] = AGLAngF(ax->c1.Angmode);	/* Angular factor of first axis     */

rfact[4] = (ax->c2.wind[1]+			/* Center of second axis    */
            ax->c2.wind[0])*0.5;
rfact[5] = ax->c2.wind[0];			/* limits of second axis    */
rfact[6] = ax->c2.wind[1];
rfact[7] = AGLAngF(ax->c2.Angmode);	/* Angular factor of second axis    */

AG_RSET(8,rfact);

AG_TRNS(t2_init,		/* Set up user defined transformation       */
        t2_direct,
        t2_reverse);
t2_init();
					/* Compute cartesian window          */
					/* and transformation center         */
ac1=ax->c1.wind[0];
ac2=0.0;
t2_direct(&ac1,&ac2);
ax->Wind[0]=ac1;
ax->Wind[1]= -ac1;

ac1=ax->c1.wind[0];
ac2=ax->c2.wind[1];
t2_direct(&ac1,&ac2);
ax->Wind[2]= -ac2;
ax->Wind[3]= ac2;

return SUCCESS;
}


/****************************************************************************/
/*                            SIN      routines  Stereographics             */

static void sin_init()
{
float fact[8];

(void)AG_RGET("user",fact);	/* retrieve transformation parameters       */
c1ofs = fact[0];
c1_min= fact[1];
c1_max= fact[2];
c1fact= fact[3];
c2ofs = fact[4];
c2_min= fact[5];
c2_max= fact[6];
c2fact= fact[7];
}


static int sin_direct(c1,c2)	/* Convert a (c1,c2) value into (x,y)       */
double *c1, *c2;		/* Longitude,latitude                       */
{
register double ang;
register double cos1,cos2;
int retval = 0;

cos1 = *c1;
cos2 = *c2;

retval |= BOUNDARY1(cos1,c1_min,c1_max);
retval |= BOUNDARY2(cos2,c2_min,c2_max);

ang = c2fact*(cos2-c2ofs);	/* Center transformation longitude          */
*c2 = sin(ang);

cos2=cos(ang);
ang = c1fact*(cos1-c1ofs);	/* Center transformation latitude           */

*c1 = sin(ang)*cos2;

return retval;
}


static int sin_reverse(c1,c2)	/* Reverse arcmonic conversion               */
double *c1, *c2;
{
int retval=0;
register double X= *c1,Y= *c2;

X=atan2(X,sqrt(1.0-X*X-Y*Y));

Y=asin(Y);

X = X/c1fact+c1ofs;
Y = Y/c2fact+c2ofs;

retval |= BOUNDARY1(X,c1_min,c1_max);
retval |= BOUNDARY2(Y,c2_min,c2_max);

*c1=X;
*c2=Y;

return retval;
}


static int chk_sin(ax)		/* Check sin transformation                 */
struct NLN_STATUS *ax;
{
ax->c1.mode = LIN;
ax->c2.mode = ANG;

if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,INT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);
return SUCCESS;
}


static int make_sin(ax)		/* Set up the stereographic transformation */
struct NLN_STATUS *ax;
{
float rfact[8];
double ac1,ac2;

rfact[0] = (ax->c1.wind[1]+			/* Center of first axis     */
            ax->c1.wind[0])*0.5;
rfact[1] = ax->c1.wind[0];			/* limits of first axis     */
rfact[2] = ax->c1.wind[1];
rfact[3] = AGLAngF(ax->c1.Angmode);	/* Angular factor of first axis     */

rfact[4] = (ax->c2.wind[1]+			/* Center of second axis    */
            ax->c2.wind[0])*0.5;
rfact[5] = ax->c2.wind[0];			/* limits of second axis    */
rfact[6] = ax->c2.wind[1];
rfact[7] = AGLAngF(ax->c2.Angmode);	/* Angular factor of second axis    */

AG_RSET(8,rfact);

AG_TRNS(sin_init,		/* Set up user defined transformation       */
        sin_direct,
        sin_reverse);
sin_init();
					/* Compute cartesian window          */
					/* and transformation center         */
ac1=ax->c1.wind[0];
ac2=0.0;
sin_direct(&ac1,&ac2);
ax->Wind[0]=ac1;
ax->Wind[1]= -ac1;

ac1=ax->c1.wind[0];
ac2=ax->c2.wind[1];
sin_direct(&ac1,&ac2);
ax->Wind[2]= -ac2;
ax->Wind[3]= ac2;

return SUCCESS;
}


/****************************************************************************/
/*                           Some generic utilities                         */

static int make_some(ax)	/* Set up some transformations            */
struct NLN_STATUS *ax;
{
float rfact[8];
double ac1,ac2;
int geom;
int (*trn)();

ac1 = AGLAngF(ax->c1.Angmode);
rfact[0] = (ax->c1.wind[1]+			/* Center of first axis     */
            ax->c1.wind[0])*0.5;
ac2 = 1.0001*AG_PI/ac1;
rfact[1] = rfact[0]-ac2;			/* limits of first axis     */
rfact[2] = rfact[0]+ac2;
rfact[3] = ac1;				/* Angular factor of first axis     */

ax->c1.ftik= rfact[1];
ax->c1.ltik= rfact[2];
ax->c1.tikmj= 0.785398163397448/ac1;

ac1 = AGLAngF(ax->c2.Angmode);	
rfact[4] = (ax->c2.wind[1]+			/* Center of second axis    */
            ax->c2.wind[0])*0.5;
ac2 = 1.0001*AG_PI05/ac1;
rfact[5] = rfact[4]-ac2;			/* limits of second axis    */
rfact[6] = rfact[4]+ac2;
rfact[7] = ac1;				/* Angular factor of second axis    */

AG_RSET(8,rfact);

switch(ax->Proj) {		/* Set up user defined transformation       */
case AITOFF:
	AG_TRNS(ait_init,
        	ait_direct,
        	ait_reverse);
	ait_init();
	trn = ait_direct;
	geom=TRUE;
	break;
case GLS:
	AG_TRNS(gno_init,
        	gls_direct,
        	gls_reverse);
	gno_init();
	geom=TRUE;
	trn = gls_direct;
	break;
case MERCATOR:
	AG_TRNS(gno_init,
        	mrc_direct,
        	mrc_reverse);
	gno_init();
	trn = mrc_direct;
	geom=FALSE;
	break;
case S2:
	AG_TRNS(gno_init,
        	s2_direct,
        	s2_reverse);
	gno_init();
	geom=TRUE;
	trn = s2_direct;
	break;
default:
	return FAILURE;
}
					/* Compute cartesian window          */
					/* and transformation center         */
ac1=ax->c1.wind[0];
ac2=0;
trn(&ac1,&ac2);
ax->Wind[0]=ac1;

ac1=ax->c1.wind[1];
ac2=0;
trn(&ac1,&ac2);
ax->Wind[1]=ac1;

ac1=0;
ac2=ax->c2.wind[0];
trn(&ac1,&ac2);
ax->Wind[2]=ac2;

ac1=0;
ac2=ax->c2.wind[1];
trn(&ac1,&ac2);
ax->Wind[3]=ac2;

if(geom)
	MSSET("geom",ax->Metamode);


return SUCCESS;
}

static void plot_title(ax)
struct NLN_STATUS *ax;
{
if(*(ax->Title) != '\0') {
	char Ssset[40];
	float td=ax->Txtdim*1.5;
	float xc,yc;

	sprintf(Ssset,"lfrg;chdim=%5.2f,%5.2f;",td,td);
	MSSET(Ssset,ax->Metamode);
	xc = (ax->Boxa[1] + ax->Boxa[0])*0.5;
	yc = ax->Boxa[3];
	AG_GTXT(xc,yc,ax->Title,20);
}
}



static void plot_labels(ax)
struct NLN_STATUS *ax;
{
if(*(ax->c1.label) != '\0') {
	float xc,yc;
	xc = (ax->Boxa[1] + ax->Boxa[0])/2.0;
	yc = ax->Boxa[2];
	AG_GTXT(xc,yc,ax->c1.label,12);
}

if(*(ax->c2.label) != '\0') {
	float xc,yc;
	yc = (ax->Boxa[2] + ax->Boxa[3])/2.0;
	xc = ax->Boxa[0];
	AG_GTXT(xc,yc,ax->c2.label,20);
}

if(*(ax->Rlabel) != '\0') {
	float xc,yc;
	yc = (ax->Boxa[2] + ax->Boxa[3])/2.0;
	xc = ax->Boxa[1];
	AG_GTXT(xc,yc,ax->Rlabel,12);
}
}


/****************************************************************************/
/*                               is12                                       */

/* Depending on the last character of keyword returns 1 (r), 2(t) or        */
/* 0 (neither)                                                              */

static int is12(token)
char *token;
{
char *ip = token;

while(TRUE) {
	if( !(isalnum(*ip)) ) break;
	if( *ip == '=' ) break;
	ip++;
}
ip--;

if( ip>=token ) 
	switch(*(ip)) { 
		case '1': return 1;
		case '2': return 2; 
	}

return 0;
}
    



/****************************************************************************/
/*                               analtok                                    */

/* Analyzes a select token                                                  */


static enum CASES analtok(token)	/* Returns an integer select code   */
char *token;				/* token to analize                 */
{
int first,last,middle,found=(-1);
enum CASES code=EOS;

static struct jmptab {
	char cmnd[3];
	char type;
	enum CASES code;
} cases[] = { 				/* TO BE MAINTAINED SORTED !!       */
	{ {'a','i','t'},	0,	AITOFF   },
	{ {'a','r','c'},	0,	ARC      },
	{ {'b','l','a'},	0,	BLAB     },
	{ {'d','e','g'},	2,	DEG12    },
	{ {'d','i','v'},	1,	DIV1     },
	{ {'f','a','c'},	1,	FACT1    },
	{ {'f','i','x'},	0,	FIXIT    },
	{ {'g','l','s'},	0,	GLS      },
	{ {'g','n','o'},	0,	GNOMONIC },
	{ {'h','o','u'},	2,	HOUR12   },
	{ {'l','l','a'},	0,	LLAB     },
	{ {'l','o','g'},	2,	LOG12    },
	{ {'m','a','n'},	1,	MAN1     },
	{ {'m','e','r'},	0,	MERCATOR },
	{ {'n','g','r'},	2,	NGRID12  },
	{ {'n','q','u'},	2,	NQUOT12  },
	{ {'o','f','f'},	1,	OFST1    },
	{ {'p','o','l'},	0,	POLAR    },
	{ {'q','d','e'},	2,	QDEG12   },
	{ {'q','h','o'},	2,	QHOUR12  },
	{ {'q','r','a'},	2,	QRAD12   },
	{ {'r','a','d'},	2,	RAD12    },
	{ {'r','l','a'},	0,	RLAB     },
	{ {'s','2','\0'},	0,	S2       },
	{ {'s','i','n'},	0,	SIN      },
	{ {'s','t','e'},	0,	STEREO   },
	{ {'t','i','t'},	0,	TITLE    },
	{ {'t','x','d'},	0,	TXDIM    },
	{ {'w','i','n'},	0,	WINDOW   }
};

if(*token=='\0') 
	return EOS;

first=0;
last=(sizeof(cases)/sizeof(struct jmptab))-1;

while ((last>=first)&&(found<0)) {
	int cmp;
	middle = (last+first)/2;
	cmp=strncmp(token,cases[middle].cmnd,3);
	if(cmp < 0)
		last=middle-1;
	else if (cmp > 0)
		first=middle+1;
	else
		found=middle;
}
if(found>=0) {
	char *getrt = token+2;
	int rt;

	switch(cases[found].type) {
		
		case 1:
			rt=is12(getrt);
			if(rt!=0)
				code=(enum CASES)((int)cases[found].code+rt-1);
			break;
		case 2:
			rt=is12(getrt);
			code=(enum CASES)((int)cases[found].code+rt);
			break;
		case 0:
			code=cases[found].code;
			break;
	}
}
return code;
} 



/****************************************************************************/
/*                              setup_nlin                                  */

static int setup_nlin (sel,token,ax)	/*  Set status according to         */
					/* select code                      */
enum CASES sel;			/* select code                              */
char *token;			/* select token (to get parameters)         */
struct NLN_STATUS *ax;		/* Status structure                         */
{				/* Returns FALSE if no more tokens to anal. */
int eos = FALSE;
int nvals;
float rbuf[4];

switch(sel) {
case MAN1:  	/*****  Set (manual) tiks values on R-axis  **********/
	nvals=2;
	nvals=AG_RVAL(token,nvals,rbuf);

	if(nvals>=1) { 
		ax->c1.man=MANUAL; 
		ax->c1.tikmj=rbuf[0]; 
		if(ax->c1.tikmj<=0.0) { 
			ax->c1.man=NONE; 
			if(*ax->c1.form!='*')
				strcpy(ax->c1.form,FLOAT); 
		}
	}
	if(nvals>=2) ax->c1.tikmn=rbuf[1];
	break;
case MAN2:  	/*****  Set (manual) tiks values on Y-axis  **********/
	nvals=2;
	nvals=AG_RVAL(token,nvals,rbuf);

	if(nvals>=1) { 
		ax->c2.man=MANUAL; 
		ax->c2.tikmj=rbuf[0]; 
		if(ax->c2.tikmj<=0.0) { 
			ax->c2.man=NONE; 
			if(*ax->c2.form!='*')
				strcpy(ax->c2.form,FLOAT); 
		}
	}
	if(nvals>=2) 
		ax->c2.tikmn=rbuf[1];
	break;
				/* Set up angular interpretation         */
case DEG12:
	ax->c1.Angmode=DEGREES;
	ax->c2.Angmode=DEGREES;
	break;
case DEG1:
	ax->c1.Angmode=DEGREES;
	break;
case DEG2:
	ax->c2.Angmode=DEGREES;
	break;
case HOUR12:
	ax->c1.Angmode=HOURS;
	ax->c2.Angmode=HOURS;
	break;
case HOUR1:
	ax->c1.Angmode=HOURS;
	break;
case HOUR2:
	ax->c2.Angmode=HOURS;
	break;

case RAD12:
	ax->c1.Angmode=RADIANS;
	ax->c2.Angmode=RADIANS;
	break;
case RAD1:
	ax->c1.Angmode=RADIANS;
	break;
case RAD2:
	ax->c2.Angmode=RADIANS;
	break;
				/* Set up quote angular formats          */
case QDEG12:
	strcpy(ax->c1.form,DDMM);
	strcpy(ax->c2.form,DDMM);
	break;
case QDEG1:
	strcpy(ax->c1.form,DDMM);
	break;
case QDEG2:
	strcpy(ax->c2.form,DDMM);
	break;
case QHOUR12:
	strcpy(ax->c1.form,HHMM);
	strcpy(ax->c2.form,HHMM);
	break;
case QHOUR1:
	strcpy(ax->c1.form,HHMM);
	break;
case QHOUR2:
	strcpy(ax->c2.form,HHMM);
	break;
case QRAD12:
	strcpy(ax->c1.form,FLOAT);
	strcpy(ax->c2.form,FLOAT);
	break;
case QRAD1:
	strcpy(ax->c1.form,FLOAT);
	break;
case QRAD2:
	strcpy(ax->c2.form,FLOAT);
	break;

case DIV1:
	nvals=2;
	nvals=AG_RVAL(token,nvals,rbuf);
	if(nvals>1) ax->c1.divs[1]=rbuf[1];
	if(nvals>0) ax->c1.divs[0]=rbuf[0];
	break;
case DIV2:
	nvals=2;
	nvals=AG_RVAL(token,nvals,rbuf);
	if(nvals>1) ax->c2.divs[1]=rbuf[1];
	if(nvals>0) ax->c2.divs[0]=rbuf[0];
	break;
case LOG12: 	/*****  Set logarithmic scale on R axis  *************/
	ax->c1.mode=LOG;
	ax->c2.mode=LOG;
	break;
case LOG1:
	ax->c1.mode=LOG;
	break;
case LOG2:
	ax->c2.mode=LOG;
	break;
case BLAB: 	/***** Set Bottom label     *************************/
	nvals=MAXLABEL;
	nvals=AG_SVAL(token,nvals,ax->c1.label); 
	break;
case RLAB: 	/***** Set right label *************************/
	nvals=MAXLABEL;
	nvals=AG_SVAL(token,nvals,ax->Rlabel);
	break;
case LLAB: 	/***** Set left label *************************/
	nvals=MAXLABEL;
	nvals=AG_SVAL(token,nvals,ax->c2.label);
	break;
case TITLE: 	/*****  Set plot title  ******************************/
	nvals=MAXTITLE;
	nvals=AG_SVAL(token,nvals,ax->Title);
	break;
case NGRID12: 	/***** Disable grid tracing on both axes  ***************/
	ax->c1.grid=FALSE; 
	ax->c2.grid=FALSE; 
	break;
case NGRID1:	/***** Disable grid tracing on 1st axis  ******************/
	ax->c1.grid=FALSE; 
	break;
case NGRID2:	/***** Disable grid tracing on 2nd axis  ******************/
	ax->c2.grid=FALSE; 
	break;
case NQUOT12: 	/***** Disable quotation on both axes  ***************/
	*ax->c1.form='*'; 
	*ax->c2.form='*'; 
	break;
case NQUOT1:	/***** Disable quotation on 1st axis  ******************/
	*ax->c1.form='*'; 
	break;
case NQUOT2:	/***** Disable quotation on 2nd axis  ******************/
	*ax->c2.form='*'; 
	break;
case FACT1: 	/*****  set mult. factor for quotation on R axis *****/
	nvals=1;
	nvals=AG_RVAL(token,nvals,rbuf);
	ax->c1.fact=rbuf[0]; 
	break;
case FACT2: 	/*****  set mult. factor for quotation on T axis *****/
	nvals=1;
	nvals=AG_RVAL(token,nvals,rbuf);
	ax->c2.fact=rbuf[0]; 
	break;
case FIXIT: 	/*****  Fix the dimension of the clipping area     ***/
	ax->Fixit=TRUE;
	break;
case OFST1: 	/*****  Set offset factor for quotation on R axis  ***/
	nvals=1;
	nvals=AG_RVAL(token,nvals,rbuf);
	ax->c1.offst=rbuf[0]; 
	break;
case OFST2: 	/*****  Set offset factor for quotation on T axis  ***/
	nvals=1;
	nvals=AG_RVAL(token,nvals,rbuf);
	ax->c2.offst=rbuf[0]; 
	break;
case TXDIM:
	nvals=1;
	nvals=AG_RVAL(token,nvals,rbuf);
	ax->Txtdim=rbuf[0];
	break;
case WINDOW:
	nvals=4;
	nvals=AG_RVAL(token,nvals,rbuf);
	if(nvals == 4){
		ax->Windfix=TRUE;
		ax->Wind[0]=rbuf[0];
		ax->Wind[1]=rbuf[1];
		ax->Wind[2]=rbuf[2];
		ax->Wind[3]=rbuf[3];
	}
	break;
case POLAR:
case GNOMONIC:
case AITOFF:
case GLS:
case S2:
case MERCATOR:
case ARC:
case STEREO:
case SIN:
	ax->Proj=(int)sel;
	break;
case EOS: 
	eos=TRUE; 
	break;
default: break;
}
return eos;
}


static void set_clpa(ax)
struct NLN_STATUS *ax;
{
char Ssset[40];
float xd[3],yd[3];

AG_RGET("VWPLIM",ax->Clpa);		/* Get current viewport limits       */

AG_RGET("SCALE",xd);

if(ax->Txtdim < 0.0) {			/* Compute string dimensions         */
	float diffx,diffy;
	diffx=ax->Clpa[1]-ax->Clpa[0];
	diffy=ax->Clpa[3]-ax->Clpa[2];
	ax->Txtdim = MIN(diffx,diffy);
} 
ax->Txtdim *= xd[0];

sprintf(Ssset,"lfrg;chdim=%5.2f,%5.2f",		/* Set up required text dim. */
              ax->Txtdim,ax->Txtdim);
MSSET (Ssset,ax->Metamode);

AG_TGET("X",xd,yd);

ax->Boxa[3] = yd[1]*7.0;		/* Set space on top                  */
ax->Boxa[2] = yd[1]*7.0;		/* Set space on bottom               */
ax->Boxa[1] = xd[0]*5.0;		/* Set space at right                */
ax->Boxa[0] = xd[0]*7.0;		/* Set     space at left             */
 
if(!ax->Fixit) {
	ax->Clpa[0] += ax->Boxa[0];	/* Compute resulting clipping area   */
	ax->Clpa[1] -= ax->Boxa[1];
	ax->Clpa[2] += ax->Boxa[2];
	ax->Clpa[3] -= ax->Boxa[3];
}

(void)AG_CDEF(ax->Clpa[0],ax->Clpa[1],	/* set up the clipping area          */
              ax->Clpa[2],ax->Clpa[3]);

}


	

/****************************************************************************/
/*                           chk_polar                                     */

static int chk_polar(ax)	/* Check polar mode                        */
				/* Returns 0 on success                    */
struct NLN_STATUS *ax;
{
ax->c1.mode = LIN;
ax->c2.mode = ANG;


if(*ax->c1.form!='*' )
	strcpy(ax->c1.form,FLOAT);

if(*ax->c2.form!='*' )
	strcpy(ax->c2.form,INT);

if(ax->c1.wind[0]<0.0)
	ax->c1.wind[0] = 0.0;

if(ax->c1.wind[1]<=ax->c1.wind[0]) {
	AG_GERR(201,Range);
	return FAILURE;
}

return SUCCESS;
}

	


static int chk_proj(ax)
struct NLN_STATUS *ax;
{
int retval=FAILURE;

switch(ax->Proj) {
case POLAR:
	retval = chk_polar(ax);
	break;
case GNOMONIC:
	retval = chk_gnomo(ax);
	break;
case AITOFF:
	retval = chk_aitoff(ax);
	break;
case GLS:
	retval = chk_gls(ax);
	break;
case MERCATOR:
	retval = chk_mrc(ax);
	break;
case S2:
	retval = chk_s2(ax);
	break;
case ARC:
	retval = chk_arc(ax);
	break;
case STEREO:
	retval = chk_t2(ax);
	break;
case SIN:
	retval = chk_sin(ax);
	break;
default:
	AG_GERR(202,"Illegal tranformation");
	retval = FAILURE;
	break;
}
return retval;
}


/****************************************************************************/
/*                             do_proj                                     */

static void do_proj(ax)		/* Draw the plot in gnomomonic coordinates  */
struct NLN_STATUS *ax;
{
float data[11];
float *cc1,*cc2;

int qdisp=0;
int qdelay1=FALSE;
int qdelay2=FALSE;
int stat=0;

void (*Draw_func)();



cc1 = data;
cc2 = data+5;

set_clpa(ax);

AGLfmt(&(ax->c1));
AGLfmt(&(ax->c2));

switch(ax->Proj) {

case POLAR:
	if((ax->c2.Angmode==RADIANS)&&(*ax->c2.form!='*'))
		strcpy(ax->c2.form,"%f");
	qdelay2=TRUE;			/* Disable quotes on theta axis      */
	qdisp=QUOTE_DISPL;		/* To get displaced quote positions  */
	stat=make_polar(ax);
	break;

case GNOMONIC: 				/* Check data window range           */
	if(ltrange(&(ax->c1),AG_PI)!=SUCCESS)
		return;
	if(ltrange(&(ax->c2),AG_PI)!=SUCCESS)
		return;

	stat=make_gnomo(ax);		/* Set up things for gnomonic axis   */
	break;

case ARC: 				/* Check data window range           */
	if(ltrange(&(ax->c1),AG_PI)!=SUCCESS)
		return;
	if(ltrange(&(ax->c2),AG_PI)!=SUCCESS)
		return;

	stat=make_arc(ax);
	break;

case STEREO: 				/* Check data window range           */
	if(lerange(&(ax->c1),AG_PI05)!=SUCCESS)
		return;
	if(lerange(&(ax->c2),AG_PI05)!=SUCCESS)
		return;

	stat=make_t2(ax);
	qdelay1=TRUE;
	qdelay2=TRUE;
	break;
case SIN: 				/* Check data window range           */
	if(lerange(&(ax->c1),AG_PI05)!=SUCCESS)
		return;
	if(lerange(&(ax->c2),AG_PI05)!=SUCCESS)
		return;

	qdelay1=TRUE;
	qdelay2=TRUE;
	stat=make_sin(ax);
	break;

case AITOFF: 				/* Check data window range           */
case GLS: 
case S2: 
	stat=make_some(ax);
	qdelay1=TRUE;
	qdelay2=TRUE;
	break;
case MERCATOR: 
	stat=make_some(ax);
	break;
}

if(stat!=SUCCESS) {
	AG_GERR(203,"Transformation setup");
	return;
}
	
AG_WDEF(ax->Wind[0],ax->Wind[1],	/* Define cartesian Window limits    */
        ax->Wind[2],ax->Wind[3]);

AG_RGET("CLPLIM",data);

ax->Boxa[0] = data[0]-ax->Boxa[0];    		/* box enclosing everything. */
ax->Boxa[1] = data[1]+ax->Boxa[1];
ax->Boxa[2] = data[2]-ax->Boxa[2]; 
ax->Boxa[3] = data[3]+ax->Boxa[3];   

MSSET("special",ax->Metamode);	/* reset SPECIAL mode spoiled by AG_WDEF()   */

					/* Now draw everything               */

if(ax->c1.mode==ANG) 
	Draw_func=AG_GINT;
else
	Draw_func=AG_GPLL;
	
cc1[0]=ax->c1.wind[0];
cc1[1]=ax->c1.wind[1];
cc2[0]=ax->c2.wind[0];
cc2[1]=cc2[0];
if(ax->c1.grid)
	Draw_func(cc1,cc2,2);

cc2[0]=ax->c2.wind[1];
cc2[1]=cc2[0];
if(ax->c1.grid)
	Draw_func(cc1,cc2,2);

if(ax->c2.mode==ANG) 
	Draw_func=AG_GINT;
else
	Draw_func=AG_GPLL;
	
cc1[0]=ax->c1.wind[0];
cc1[1]=cc1[0];
cc2[0]=ax->c2.wind[0];
cc2[1]=ax->c2.wind[1];

if(ax->c2.grid)
	Draw_func(cc1,cc2,2);

cc1[0]=ax->c1.wind[1];
cc1[1]=cc1[0];
if(ax->c2.grid)
	Draw_func(cc1,cc2,2);


if((ax->c1.man!=NONE)&&ax->c1.grid) {
	data[0] = ax->c1.wind[0];
	data[1] = ax->c1.wind[1];
	data[2] = ax->c1.ftik;
	data[3] = ax->c1.ltik;
	data[4] = ax->c1.tikmj;
	data[5] = 0.0;
	data[6] = ax->c2.wind[0];
        data[7] = ax->c2.wind[1];
	data[8] = ax->c1.offst;
	data[9] = qdelay1 ? 0 : ax->c1.fact;
	data[10]= ax->c1.iexp;
	AG_AXIS(qdisp|ax->Bold|X_BOTTOM_AXIS|NO_AXIS_LINE,
	        data,0.0,ax->c1.form,ax->c1.label);
}

if((ax->c2.man!=NONE)&&ax->c2.grid) {
	data[0] = ax->c2.wind[0];
	data[1] = ax->c2.wind[1];
	data[2] = ax->c2.ftik;
	data[3] = ax->c2.ltik;
	data[4] = ax->c2.tikmj;
	data[5] = 0.0;
	data[6] = ax->c1.wind[0];
        data[7] = ax->c1.wind[1];
	data[8] = ax->c2.offst;
	data[9] = qdelay2 ? 0 : ax->c2.fact;
	data[10]= ax->c2.iexp;
	AG_AXIS(ax->Bold|Y_LEFT_AXIS|NO_AXIS_LINE,
	        data,0.0,ax->c2.form,ax->c2.label);
}

switch(ax->Proj) {

case GNOMONIC: 				/* quotes have been written already  */
case ARC:
case MERCATOR: 
	break;

case POLAR:				/* Special way to label c2 axis      */
	if((ax->c2.man!=NONE)&&(*ax->c2.form!='*'))
		polar_quote(ax);
	break;
case AITOFF:				/* Need a special way to draw quotes */
case SIN:
case GLS:
case S2: 
case STEREO:
	if(ax->c1.man != NONE) {
		data[0] = 0.0;
		data[1] = 0.0;
		data[2] = ax->c1.ftik;
		data[3] = ax->c1.ltik;
		data[4] = ax->c1.tikmj;
		data[5] = 0.0;
		data[6] = (ax->c2.wind[0]+ax->c2.wind[1])*0.5;
        	data[7] = data[6];
		data[8] = ax->c1.offst;
		data[9] = ax->c1.fact;
		data[10]= ax->c1.iexp;
		AG_AXIS(ax->Bold|QUOTE_DISPL|X_BOTTOM_AXIS,
		        data,0.0,ax->c1.form,"");
	}

	if(ax->c2.man != NONE) {
		data[0] = 0.0;
		data[1] = 0.0;
		data[2] = ax->c2.ftik;
		data[3] = ax->c2.ltik;
		data[4] = ax->c2.tikmj;
		data[5] = 0.0;
		data[6] = (ax->c1.wind[0]+ax->c1.wind[1])*0.5;
        	data[7] = data[6];
		data[8] = ax->c2.offst;
		data[9] = ax->c2.fact;
		data[10]= ax->c2.iexp;
		AG_AXIS(ax->Bold|QUOTE_DISPL|Y_LEFT_AXIS,
		        data,0.0,ax->c1.form,"");
	}
	break;
}

MSSET("normal",ax->Metamode);
plot_labels(ax);				/* Draw labels               */

plot_title(ax);					/* Draw plot title           */

MSSET ("special;chbasic;sybasic",ax->Metamode); 
AG_VUPD();
}



/****************************************************************************/
/*++                          AG_NLIN (User callable)                       */

/* AG_NLIN                                                                  */


/* This module computes and draws a grid on the currently active viewport   */
/* using one of a set of non-linear coordinate projections.                 */

/* The position of the grid is computed so that quotes, labels and title    */
/* will fit into viewport.                                                  */

/* A variety of options and axes drawing modes can be selected by means of  */
/* an option selection string, this will also select the actual projection  */
/* to be applied.                                                           */

/* After the call the the selected projection will remain active, so that   */
/* subsequent graphic operations will be referred to the selecte corrdinate */
/* system.                                                                  */

/* After the plot is drawn, the box enclosing it may be addressed by re-    */
/* storing the cartesian transformation via a call:                         */

/*              AG_TRNS(NULL,NULL,NULL)                                     */

/* After the call the graphic status will be "USER" and a window suitable   */
/* to enclose the polar plot is selected (see also WINDOW below)            */


/* N.B.: Character dimensions and orientation as set prior of the call are  */
/*       not preserved                                                      */
 

void AG_NLIN(c1l,c1u,c2l,c2u,select)

 double c1l,c1u;              /* User window limits on first  axis           */
                              /* Limits depend on the actual transformation  */
                              /* selected.                                   */
 double c2l,c2u;              /* User window limits on second  axis          */
                              /* Limits depend on the actual transformation  */
                              /* selected.                                   */
                              /* Actual axes meaning depends on the particu- */
                              /* lar projection selected.                    */
                              /* Angular values are interpreted as Radians   */
                              /* by default, but it can be changed to        */
                              /* Degrees or Hours (See items: DEG and HOUR   */
                              /*  below)                                     */
 char *select;                /* Option string (see below)                   */

/* Option string specifications:                                             */

/* Syntax:    "keywd[=value,value...][;keywd[=value...]]"                    */
 
/* The keyword spacifying the type of transformation is required, it must be */
/* one of the following:                                                     */

/* AITOFF                                                                    */
/*    Selects the transformation:                                            */

/*    X=2*cos(c2)sin(c1/2)/d                                                 */
/*    Y=sin(c2)/d                                                            */

/*    with: d=sqrt((1+cos(c2)cos(c1/2))/2)                                   */

/*    This transformation will map the sphere onto an ellipse with conser-   */
/*    vation of surfaces                                                     */

/*    When this transformation is selected the user window arguments are only*/
/*    used in order to determine the center of the projection, the projection*/
/*    limits will always be [-180,+180] degrees on first axis and [-90,+90]  */
/*    on the second axis.                                                    */

/* ARC                                                                       */
/*    Selects the arc transformation (Schmidt plates projection)             */

/*    X=(sin(c1)cos(c2))*acos((cos(c1)cos(c2)))/d                            */
/*    Y=sin(c2)*acos((cos(c1)cos(c2)))/d                                     */

/*    where:  d=sqrt((sin(c1)cos(c2))**2+sin(c2)**2)                         */

/* GLS                                                                       */
/*    Selects the Global Sinusoidal transformation:                          */

/*    X=c1*cos(c2)                                                           */
/*    Y=c2                                                                   */

/*    This transformation will map the sphere into two opposite sinusoids    */

/* GNOMONIC                                                                  */
/*    Selects gnomonic transformation:                                       */

/*    X=((sin(c1)cos(c2))/(cos(c1)cos(c2)))                                  */
/*    Y=(sin(c2)/(cos(c1)cos(c2)))                                           */
/*    The transformation is centered in the center point of the defined data */
/*    window.                                                                */

/*    Limits:  (maxc1 - minc1) < pi                                          */
/*             (maxc2 - minc2) < pi                                          */

/* MERCATOR                                                                  */

/*    Selects the Mercator transformation:                                   */

/*    X=c1                                                                   */
/*    Y=atanh(sin(c2))                                                       */

/* POLAR                                                                     */
/*    Selects polar Transformation.                                          */

/*    X=c1*cos(c2)                                                           */
/*    Y=c1*sin(c2)                                                           */

/*    Limits: 0 < minc1 < maxc1                                              */

/* S2                                                                        */
/*    selects the sin2 transformation:                                       */

/*    X=(sin(c1)cos(c2))*sqrt(2/(1+(cos(c1)cos(c2))))                        */
/*    Y=sin(c2)*sqrt(2/(1+(cos(c1)cos(c2))))                                 */

/*    This transformation will map the sphere onto a circle                  */

/* SIN                                                                       */
/*    Selects the Sin transformation:                                        */

/*    X=(sin(c1)cos(c2))                                                     */
/*    Y=sin(c2)                                                              */

/* STEREO                                                                    */
/*    Selects the stereographic transformation:                              */

/*    X=(2(sin(c1)cos(c2))/(1+(cos(c1)cos(c2))))                             */
/*    Y=(2*sin(c2)/(1+(cos(c1)cos(c2))))                                     */


/* The following keywords are not required in that suitable defaults are     */
/* provided:                                                                 */

/* DEG                                                                       */
/* DEG1                                                                      */
/* DEG2                                                                      */
/*    Select interpretation of values on the selected axis (or both) to      */
/*    degrees.                                                               */

/*    Default interpretation depends on the particular transformation        */
/*    selected.                                                              */

/* DIV1=[start[,stop]]                                                       */
/* DIV2=[start[,stop]]                                                       */
/*    Draw divisions and corresponding quotes only between given coordinates */

/* FACT1                                                                     */
/* FACT2                                                                     */
/*    Specifies a multiplying factor for quotes. Numbers written at tik      */
/*    marks are computed as follows:                                         */

/*           quote = offst? + ( coord * fact? )                              */

/*    where: coord is the coordinate value at the tik,                       */
/*           fact? is the factor set with this command (? = either 1 or 2)   */
/*           offst? is an offset value (see OFFST1, OFFST2)                  */
/*           quote is the numerical value actually written at the tik        */

/*    Default is 1.0                                                         */

/* FIXIT                                                                     */
/*    Forces the plot area (i.e.: the axes box) to cover exactly the current */
/*    clipping area, instead of computing a new clipping area in order to    */
/*    fit all the plot (quotes, labels, title, included) in the clipping     */
/*    area. When using this mode the caller must define a clipping area      */
/*    suitably smaller than the current viewport if everything that must be  */
/*    plotted outside the clipping area (quotes and so on ) is to be seen    */

/* HOUR                                                                      */
/* HOUR1                                                                     */
/* HOUR2                                                                     */
/*    Select interpretation of values on the selected axis (or both) to      */
/*    hours.                                                                 */
/*                                                                           */
/*    Default interpretation depends on the particular transformation        */
/*    selected.                                                              */

/* LABB=string                                                               */
/*    Specifies a label to be written on the bottom of plot                  */

/* LABL=string                                                               */
/*    Specifies a label to be written to the left of plot                    */

/* LOG                                                                       */
/* LOG1                                                                      */
/* LOG2                                                                      */
/*    Select logarithmic transformation on the selected axis (or both)       */

/*    Default is Linear                                                      */

/* MAN1=mjstep[,mnstep]                                                      */
/* MAN2=mjstep[,mnstep]                                                      */
/*    Select manual definition of tik positions. Mjstep define the spacing   */
/*    of major divisions; mnstep define the spacing of minor divisions. If   */
/*    mjstep is <= 0.0, then no divisions are traced; if mnstep is not       */
/*    specified, minor divisions are not traced.                             */
/*    If logarithmic transformation is selected for the axis, mjstep and     */
/*    mnstep are multiplicative increments for major and minor divisions,    */
/*    respectively. Mjstep must be a power of 10 (but not equal to 1.0), and */
/*    mnstep may be either 5 or a power of 10 not equal to 1.0.              */

/*    The default is automatic tik computation.                              */

/* NGRID                                                                     */
/* NGRID1                                                                    */
/* NGRID2                                                                    */
/*    do not draw grid lines along the corresponding axis                    */

/* NQUOT                                                                     */
/* NQUOT1                                                                    */
/* NQUOT2                                                                    */
/*    Suppresses quotes on the corresponding axis.                           */

/* OFFST1                                                                    */
/* OFFST2                                                                    */
/*    Offset for quotes. Values written at tik marks are computed as:        */

/*           quote = offst? + ( coord * fact? )                              */

/*    where: coord is the coordinate value at the tik,                       */
/*           fact? is the multiplicative factor (see FACTR, FACTT)           */
/*           offst? is an offset set with this command (? = either x or y)   */
/*           quote is the numerical value actually written at the tik        */

/*    Default is 0.0                                                         */

/* QDEG                                                                      */
/* QDEG1                                                                     */
/* QDEG2                                                                     */
/*    Select degrees format for quotes on angular valued axis                */
/*    It also affects preferred tiks positions when automatic tik computing  */
/*    is enabled.                                                            */

/*    Default format depends on the particular transformation selected.      */

/* QHOUR                                                                     */
/* QHOUR1                                                                    */
/* QHOUR2                                                                    */
/*    Select hour format for quotes on angular valued axis                   */
/*                                                                           */
/*    Default format depends on the particular transformation selected.      */

/* QRAD                                                                      */
/* QRAD1                                                                     */
/* QRAD2                                                                     */
/*    Select radiant format for quotes on angular valued axis                */
/*    It also affects preferred tiks positions when automatic tik computing  */
/*    is enabled.                                                            */

/*    Default format depends on the particular transformation selected.      */

/* RAD                                                                       */
/* RAD1                                                                      */
/* RAD2                                                                      */
/*    Select interpretation of values on the selected axis (or both) to      */
/*    radiants.                                                              */
/*                                                                           */
/*    Default interpretation depends on the particular transformation        */
/*    selected.                                                              */

/* TITLE=string                                                              */
/*    Specifies a title to be written on top of the plot.                    */

/* TXDIM=mult                                                                */
/*    Specifies a multiplying factor to apply to all text strings            */

/* WINDOW=xmin,xmax,ymin,ymax                                                */
/*    Specifies limits for the cartesian window. E.g.: WINDOW=-1,1,-1,1      */
/*    would enclose all the four quadrants of a polar plot, WINDOW=0,1,0,1   */
/*    would enclose the first quadrant only, and so on.                      */
/*    By default the window limits are automatically adjusted.               */

/* Here follows a typical calling sequence:                                  */
 
/*    AG_VDEF (.......)                   Define graphic device              */
/*    AG_NLIN(r0,r1,t0,t1,"polar")        Define and draw axes               */
/*    ... plot data ....                                                     */
/*    AG_CLS()                            Close AGL                          */


/* REMARKS:                                                                  */

/*   Title and label strings may contain AGL standard metacharacters for     */
/*   superscripts, subscripts and font definition (See: AG_GTXT)             */

                                                                         /*--*/
{
int eos;
char *ix = select;

struct NLN_STATUS my_status;

{
	char aux[64];
	sprintf(aux,"AG_NLIN(%g,%g,%g,%g,",c1l,c1u,c2l,c2u);
	(void)AG_DMSG(aux,select);
}


my_status.c1.wind[0] = c1l; 
my_status.c1.wind[1] = c1u;
my_status.c2.wind[0] = c2l; 
my_status.c2.wind[1] = c2u;

my_status.c1.divs[0] = c1l; 
my_status.c1.divs[1] = c1u;
my_status.c2.divs[0] = c2l; 
my_status.c2.divs[1] = c2u;

init_gen(&my_status);

eos=FALSE;
while ( ! eos ) {				/* String analysis loop     */
	enum CASES sel;
	char token[MAXTOKEN+1];

	ix = AG_SCAN(ix,';',MAXTOKEN,token);	/* Parse next token         */
	sel=analtok(token);

	eos = setup_nlin(sel,token,&my_status);
}

if(chk_proj(&my_status)==SUCCESS)
	do_proj(&my_status);
}

