/*======================= MiscMathCoordConverter.m ==========================*/
/* MiscMathCoordConverter class converts between various standard
   Mathematical coordinate systems. All values are double precision floating
   point numbers representing locations in a three dimensional coordinate 
   system.

   There is only one instance ever, so unless changes are made, this class
   is NON REENTRANT.

   DMA Release 0.8, Copyright @1993 by Genesis Project, Ltd. All Rights
   Reserved. For further information on terms and conditions see
		the MiscKit license.

HISTORY
10-Mar-93  Dale Amon at GPL
	   Created.
*/

#import <ansi/math.h>
#import <misckit/miscgiskit.h>

@implementation MiscMathCoordConverter

/*===========================================================================*/
/* Internal methods
   These are applied to each data point by a applyTransform:
*/
/*===========================================================================*/
/* Internal methods to convert a point to a different coordinate system.
   Input vector is pointed to by src and the result of the transformation
   is stored in dst.
*/

- (void) cartesianToCylindrical
  {	dst[MISC_THETA_CYL]  = atan(src[MISC_Y_COORD]/src[MISC_X_COORD]);
	dst[MISC_RADIUS_CYL]	= sqrt(src[MISC_X_COORD] * src[MISC_X_COORD] +
			       src[MISC_Y_COORD] * src[MISC_Y_COORD]);
	dst[MISC_Z_CYL]	= src[MISC_Z_COORD];
  }

- (void) sphericalToCylindrical
  {	dst[MISC_THETA_CYL]	= src[MISC_THETA_SPHERE];
	dst[MISC_RADIUS_CYL]	= src[MISC_RHO_SPHERE] * sin(src[MISC_PHI_SPHERE]);
	dst[MISC_Z_CYL]	= src[MISC_RHO_SPHERE] * cos(src[MISC_PHI_SPHERE]);
  }

- (void) cartesianToSpherical
  {	double	xsq,ysq;

	xsq = src[MISC_X_COORD] * src[MISC_X_COORD];
	ysq = src[MISC_Y_COORD] * src[MISC_Y_COORD];

	dst[MISC_RHO_SPHERE]   = sqrt(xsq + ysq +
			src[MISC_Z_COORD] * src[MISC_Z_COORD]);
	dst[MISC_THETA_SPHERE] = atan(src[MISC_Y_COORD]/src[MISC_X_COORD]);
	dst[MISC_PHI_SPHERE]   = atan(sqrt(xsq + ysq)/src[MISC_Z_COORD]);
  }

- (void) cylindricalToSpherical
  {	dst[MISC_RHO_SPHERE]   = sqrt(src[MISC_RADIUS_CYL] * src[MISC_RADIUS_CYL] +
				 src[MISC_Z_CYL] * src[MISC_Z_CYL]);
	dst[MISC_THETA_SPHERE] = src[MISC_THETA_CYL];
	dst[MISC_PHI_SPHERE]   = atan(src[MISC_RADIUS_CYL]/src[MISC_Z_CYL]);
  }

- (void) cylindricalToCartesian
  {	dst[MISC_X_COORD] = src[MISC_RADIUS_CYL] * cos(src[MISC_THETA_CYL]);
	dst[MISC_Y_COORD] = src[MISC_RADIUS_CYL] * sin(src[MISC_THETA_CYL]);
	dst[MISC_Z_COORD] = src[MISC_Z_CYL];
  }

- (void) sphericalToCartesian
  {	dst[MISC_X_COORD] = src[MISC_RHO_SPHERE] * sin(src[MISC_PHI_SPHERE]) * 
			cos(src[MISC_THETA_CYL]);
	dst[MISC_Y_COORD] = src[MISC_RHO_SPHERE] * sin(src[MISC_PHI_SPHERE]) *
			sin(src[MISC_THETA_CYL]);
	dst[MISC_Z_COORD] = src[MISC_RHO_SPHERE] * cos(src[MISC_PHI_SPHERE]);
  }

/*===========================================================================*/
/* Class methods */
/*===========================================================================*/
/* Only one converter of this type is every needed. Of course if we got
   into a really big multiprocess agora system there might be a case
   for multiple converters. Since this object is shared by many, it
   doesn't particularly matter what zone it is allocated from.
*/

static id theMathCoordConverter = nil;

+ new
{
    if (!theMathCoordConverter) theMathCoordConverter = [[super alloc] init];
    return theMathCoordConverter;
}


/*---------------------------------------------------------------------------*/
/* Since there is only one object needed, alloc and allocFromZone: are 
   disabled and considered errors. free is simply overridden and made into a
   no op.
*/

+alloc 
 {	[self error:"  %s class should not be sent '%s' messages\n",
            [[self class] name], sel_getName(_cmd)];
	return self;
 }

+allocFromZone: (NXZone *)zone
 {	[self error:"  %s class should not be sent '%s' messages\n",
            [[self class] name], sel_getName(_cmd)];
	return self;
 }


- free {return self;}


/*===========================================================================*/
/* Initialization methods */
/*===========================================================================*/
/* We add a list of all the services which we are able to provide.
   Note that there is only one PlanetCoordConverter ever created. Once 
   initialized the same object is given to all comers and can not be destroyed.
*/

- init
  {	Class	sphere,cylinder,cartesian;

	[super init];

	sphere    = [MiscSphericalCoord   class];
	cylinder  = [MiscCylindricalCoord class];
   	cartesian = [MiscCartesianCoord   class];

	[self addService: @selector(cartesianToCylindrical)
	  convertsFrom: cartesian to: cylinder]; 

	[self addService: @selector(sphericalToCylindrical)
	  convertsFrom: sphere    to: cylinder]; 

	[self addService: @selector(cartesianToSpherical)
	  convertsFrom: cartesian to: sphere]; 

	[self addService: @selector(cylindricalToSpherical)
	  convertsFrom: cylinder  to: sphere];
 
	[self addService: @selector(cylindricalToCartesian)
	  convertsFrom: cylinder  to: cartesian];

	[self addService: @selector(sphericalToCartesian)
	  convertsFrom: sphere    to: cartesian];

	[self addService: [self fastCopySelector]
	  convertsFrom: sphere    to: sphere];

	[self addService: [self fastCopySelector]
	  convertsFrom: cylinder  to: cylinder];

	[self addService: [self fastCopySelector]
	  convertsFrom: cartesian to: cartesian];

	return self;
  }


/*===========================================================================*/
/* Archive methods */
/*===========================================================================*/

- finishUnarchiving
{
	[self free];
	return [MiscMathCoordConverter new];
}


@end
