// copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
// see COPYRIGHT for reuse legalities
//

#import "RIBSphere.h"
#import <stdio.h>


@implementation RIBSphere

+ initialize { return [RIBSphere setVersion:1], self; }

- (BOOL)hasBoundingBox { return YES; }

- init
{
  [super init];

  radius = 1.0;
  zMin = -1.0;
  zMax =  1.0;
  
  return self;
}

- setRadius:(RtFloat)newRadius 
     zMin:(RtFloat)newZMin zMax:(RtFloat)newZMax 
     thetaMax:(RtFloat)newThetaMax
     n:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector
     printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   radius = newRadius;
   zMin = newZMin;
   zMax = newZMax;
   if (zMin < (-1 * radius))
   {  zMin = -1 * radius;
   }
   if (zMax > radius)
   {  zMax = radius;
   }

   thetaMax = newThetaMax;
   [self setN:newN tokens:newTokens parms:newParms archiveVector:newArchiveVector printfTypeVector:newPrintfTypeVector printfNVector:newPrintfNVector];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- setRadius:(RtFloat)newRadius { radius = newRadius; dirtyBoundingBox = TRUE; return self; }
- setZMin:(RtFloat)newZMin { zMin = newZMin; dirtyBoundingBox = TRUE; return self; }
- setZMax:(RtFloat)newZMax { zMax = newZMax; dirtyBoundingBox = TRUE; return self; }

- (RtFloat)radius { return radius; } 
- (RtFloat)zMin { return zMin; }
- (RtFloat)zMax { return zMax; }

- calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime 
{
  if (thetaMax >= 180.0)
  {   boundingBox[0] =  -1.0 * radius;
  }
  else  // less than 180
  {  if (thetaMax >= 90.0)
     {  boundingBox[0] =  -1.0 * radius * (RtFloat)sin((double)(toRadians((thetaMax - 90.0))));
     }
     else
     {  //boundingBox[0] =  radius * (RtFloat)sin((double)(toRadians(thetaMax)));
        boundingBox[0] = 0.0;  // this is not true if zMax or abs(zMin) is less than radius
     }
  }
  boundingBox[1] = radius;

  if (thetaMax >= 270.0)
  {  boundingBox[2] = -1.0 * radius;
     boundingBox[3] = radius;
  }
  else
  {  if (thetaMax >= 180.0)
     {  boundingBox[2] = radius * sin((double)(toRadians((thetaMax))));
        boundingBox[3] = radius;
     }
     else
     {  if (thetaMax >= 90.0)
        {  boundingBox[2] = 0.0;
           boundingBox[3] = radius;
        }
        else
        {  boundingBox[2] = 0.0;
           boundingBox[3] = radius * sin((double)(toRadians((thetaMax))));
        }
     }
  }

  if (zMin < 0.0)
  {  if ((-1.0 * radius) < zMin)  // not a full sphere in negative Z
     {  boundingBox[4] = zMin;
     }
     else
     {  boundingBox[4] = -1.0 * radius;
     }
  }
  else
  {  boundingBox[4] = zMin;
  }

  if (zMax < 0.0)
  {  boundingBox[5] = zMax;
  }
  else
  {  boundingBox[5] = radius;
  }

  //fprintf(stderr, "RIBSphere bbox:\n");
  //fprintf(stderr, "\t bbox : X (%f, %f)\n", boundingBox[0], boundingBox[1]);
  //fprintf(stderr, "\t      : Y (%f, %f)\n", boundingBox[2], boundingBox[3]);
  //fprintf(stderr, "\t      : Z (%f, %f)\n", boundingBox[4], boundingBox[5]);

  dirtyBoundingBox = FALSE; 
  return self;
}

- renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  RiSphereV(radius, zMin, zMax, thetaMax, n, tokens, parms);

  return self;
}

- renderSelf:(WW3DCamera *)camera
{
  RtFloat  shutterOpenTime = [camera shutterOpenTime],
           shutterCloseTime = [camera shutterCloseTime];


  return [self renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
}


- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Sphere %f %f %f %f", radius, zMin, zMax, thetaMax);
   [super writeParameterList:stream];
   return self;
}

- writeScene:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Sphere %f %f %f %f", radius, zMin, zMax, thetaMax);
   [super writeParameterList:stream];
   return self;
}

#define typeVector "fff"
#define typeValues &radius, &zMin, &zMax

- read:(NXTypedStream*)stream 
{
    int version;
    [super read:stream];

    version = NXTypedStreamClassVersion(stream,"RIBSphere");
    if (version == 0) NXReadTypes(stream,"i",&version), version=1;
    if (version == 1)
    {  NXReadTypes(stream,typeVector,typeValues);
    } 
    else 
    {
    }
    return self;
}

- write:(NXTypedStream*)stream 
{
    [super write:stream];

    NXWriteTypes(stream,typeVector, typeValues);

    return self;
}


@end
