DLLEXPORT miBoolean domeAFL_FOV(
	miColor	*result,
	miState	*state,
	register struct dsDomeAFL_FOV *params)
{
	miScalar	fov_angle_deg = *mi_eval_scalar(&params->FOV_Angle);
	miGeoScalar	fov_angle_rad;

	miVector	viewpt_offset = *mi_eval_vector(&params->View_Offset);
	miVector	ray;

	miGeoScalar x, y, r, phi, theta;

   	/* normalize image coordinates btwn [-1,1]... */
	/* [ah] Rotate the cartesian axis 90 deg CW   */
	x = -2.0*state->raster_y/state->camera->y_resolution+1.0;
	y = 2.0*state->raster_x/state->camera->x_resolution-1.0;
	
	/* Calcaulate the radius value */
	r = MI_SQRT( ( x * x ) + ( y * y ) );

	if ( r < 1.0 ) {

		/* Calculate phi... */
		if ( (r > -EPSILON) && (r < EPSILON) ) {
			phi = 0.0;
		} else {
			phi = atan2(x,y);	// [rz] using atan2 instead of original if-then formula
		}

		/* Convert FOV angle of fisheye from degrees to radians... */
		fov_angle_rad = fov_angle_deg * M_PI / 180.0;

		/* Calculate theta... */
		
		theta = r * ( fov_angle_rad / 2.0 );

		/* Calculate Ray direction vector... */
		ray.x = (float)(sin(theta) * cos(phi));
		ray.y = (float)(-sin(theta) * sin(phi));
			/* -Z is Look At Direction*/
		ray.z = (float)(-cos(theta));

		/* Account for view offset... */
   		/* Offset is added to y & z components because 
   		   they are negative values...*/
		ray.x = ray.x - viewpt_offset.x;
		ray.y = ray.y + viewpt_offset.y;
			/* Add because MR uses -Z as Look At */
		ray.z = ray.z + viewpt_offset.z;
	
 		/* Flip the ray direction about the y-axis */
		if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
			ray.x = (-ray.x);
		}

        	/* Flip the ray direction about the x-axis */
		if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
			ray.y = (-ray.y);		
		}

		/* Convert ray from camera space */
		mi_vector_from_camera(state, &ray, &ray);

		/* Trace new ray... */
	        return(mi_trace_eye(result, state, &state->org, &ray));          
	
	} else {

		/* Set the return colors to Black */
		result->r = result->g =
		result->b = result->a = 0;
		return(miFALSE);
	}

} /* end of dome_FOV_AFL() */
DLLEXPORT miBoolean domeAFL_FOV_Stereo(
	miColor	*result,
	miState	*state,
	struct dsDomeAFL_FOV_Stereo *params)
{
	miScalar	cameras_separation_multiplier = *mi_eval_scalar(&params->Cameras_Separation_Map);
	miScalar	head_turn_multiplier = *mi_eval_scalar(&params->Head_Turn_Map);
	miScalar	head_tilt = *mi_eval_scalar(&params->Head_Tilt_Map);

	miVector	org, ray, target, htarget;
	miMatrix	tilt;
	double		x, y, r, phi, theta, rot, tmp, tmpY, tmpZ;
	double		sinP, cosP, sinT, cosT, sinR, cosR;

   	// normalize image coordinates btwn [-1,1]...
	// [rz] swap X-Y to match camera view and apply a vertical symmetry
	// [rz] basically, we rotate the cartesian axis 90deg CW
	x = -2.0*state->raster_y/state->camera->y_resolution+1.0;
	y = 2.0*state->raster_x/state->camera->x_resolution-1.0;

	// Calculate the radius value
	r = MI_SQRT((x*x)+(y*y));

	if (r < 1.0) {

		// Calculate phi...
		if ((r > -EPSILON) && (r < EPSILON) ) {
			phi = 0.0;
		} else {
			phi = atan2(y,x);
		}
		// Calculate theta...
		theta = r*(fov_angle/2.0);

		// start by matching camera (center camera)
		// mi_point_to_camera(state, &org, &state->org);
		org.x = org.y = org.z = 0.0;

		// saves common used values for performance reasons
		sinP = sin(phi); cosP = cos(phi);
		sinT = sin(theta); cosT = cos(theta);

		// center camera target vector (normalized)
		target.x = (miScalar)(sinP*sinT);
		target.y = (miScalar)(-cosP*sinT);
		target.z = (miScalar)(-cosT);

		if (camera != CENTERCAM) {
			// camera selection and initial position
			// @@@ use switch?
			if (camera == LEFTCAM) {
				org.x = (miScalar)(-cameras_separation*cameras_separation_multiplier/2);
			}
			if (camera == RIGHTCAM) {
				org.x = (miScalar)(cameras_separation*cameras_separation_multiplier/2);
			}

			if (dome_tilt_compensation) {		// tilted dome mode

				// head rotation
				// @@@ need to check atan2 params for 0 values?
				// @@@ save values of sin/cos
				tmpY = target.y*cos(-dome_tilt)-target.z*sin(-dome_tilt);
				tmpZ = target.z*cos(-dome_tilt)+target.y*sin(-dome_tilt);
				rot = atan2(target.x,-tmpY)*head_turn_multiplier;
				if (vertical_mode)
					rot *= fabs(sinP);
				sinR = sin(rot); cosR = cos(rot);

				// rotate camera
				tmp = org.x*cosR-org.y*sinR;
				org.y = (miScalar)(org.y*cosR+org.x*sinR);
				org.x = (miScalar)tmp;

				// compensate for dome tilt
				// @@@ save values of sin/cos
				tmp = org.y*cos(dome_tilt)-org.z*sin(dome_tilt);
				org.z = (miScalar)(org.z*cos(dome_tilt)+org.y*sin(dome_tilt));
				org.y = (miScalar)tmp;

				// calculate head target
				tmp = sqrt(target.x*target.x+tmpY*tmpY);
				htarget.x = (miScalar)(sin(rot)*tmp);
				htarget.y = (miScalar)(-cos(rot)*tmp);
				htarget.z = (miScalar)tmpZ;

				// dome rotation again on head target
				tmp = htarget.y*cos(dome_tilt)-htarget.z*sin(dome_tilt);
				htarget.z = (miScalar)(htarget.z*cos(dome_tilt)+htarget.y*sin(dome_tilt));
				htarget.y = (miScalar)tmp;

			} else {

				if (vertical_mode) {			// vertical mode

					// head rotation
					// @@@ need to check atan2 params for 0 values?
					rot = atan2(target.x,-target.z)*head_turn_multiplier*fabs(sinP);
					sinR = sin(rot); cosR = cos(rot);

					// rotate camera
					tmp = org.x*cosR-org.z*sinR;
					org.z = (miScalar)(org.z*cosR+org.x*sinR);
					org.x = (miScalar)tmp;

					// calculate head target
					tmp = sqrt(target.x*target.x+target.z*target.z);
					htarget.x = (miScalar)(sin(rot)*tmp);
					htarget.y = (miScalar)target.y;
					htarget.z = (miScalar)(-cos(rot)*tmp);

				} else {						// horizontal mode

					// head rotation
					rot = phi*head_turn_multiplier;
					sinR = sin(rot); cosR = cos(rot);

					// rotate camera
					tmp = org.x*cosR-org.y*sinR;
					org.y = (miScalar)(org.y*cosR+org.x*sinR);
					org.x = (miScalar)tmp;

					// calculate head target
					htarget.x = (miScalar)(sin(rot)*sinT);
					htarget.y = (miScalar)(-cos(rot)*sinT);
					htarget.z = (miScalar)target.z;

				}
			}

			// head tilt
			head_tilt = (miScalar)((head_tilt-0.5)*M_PI);
			mi_matrix_ident(tilt);
			mi_matrix_rotate_axis(tilt, &htarget, head_tilt);
			mi_vector_transform(&org, &org, tilt);

			// calculate ray from camera to target
			target.x *= dome_radius;
			target.y *= dome_radius;
			target.z *= dome_radius;
			ray.x = target.x-org.x;
			ray.y = target.y-org.y;
			ray.z = target.z-org.z;
			mi_vector_normalize(&ray);

		} else {		// center camera

			ray = target;

		}


		// Account for view offset...
   		// Offset is added to y & z components because they are negative values...
		// @@@ ray.x = ray.x - viewport_offset.x;
		// @@@ ray.y = ray.y + viewport_offset.y;
		// @@@ ray.z = ray.z + viewport_offset.z;

//mi_debug("II->,Phi=%f,Theta=%f,rot=%f,camx=%f,camy=%f", (miScalar)phi, (miScalar)theta, (miScalar)rot, (miScalar)org.x, (miScalar)org.y);

		// Flip the ray direction about the y-axis
		// @@@ if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
		// @@@ 	ray.x = (-ray.x);
		// @@@ }
       	// Flip the ray direction about the x-axis
		// @@@ if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
		// @@@ 	ray.y = (-ray.y);		
		// @@@ }


		// Convert ray from camera space
		mi_vector_from_camera(state, &ray, &ray);
		mi_point_from_camera(state, &org, &org);

		// Trace new ray...
		return(mi_trace_eye(result, state, &org, &ray));

	} else {

		// Set the return colors to Black
		result->r = result->g = result->b = result->a = 0;
		return(miFALSE);
	}

}
DLLEXPORT miBoolean domeAFL_WxH(
	miColor	*result,
	miState	*state,
	register struct dsDomeAFL_WxH *params)
{
	miScalar	diameter = *mi_eval_scalar(&params->Diameter);
	miScalar	height   = *mi_eval_scalar(&params->Height);
	
	miGeoScalar	fov;	    /* Field-of-View of specified dome */
	miGeoScalar	radius;	    /* Radius of dome being subtended */
	
	/* Does this need to be a pointer? */
	miVector	viewpt_offset = *mi_eval_vector(&params->View_Offset);
	miVector	ray;

	miGeoScalar	x, y, r, phi, theta;

	/* normalize image coordinates btwn [-1,1]... */
	x = (2.0 * state->raster_x) / state->camera->x_resolution - 1.0;
	y = (2.0 * state->raster_y) / state->camera->y_resolution - 1.0;

	/* Calculate FOV for given Diameter & height of dome...          */
	/* Equations obtained from:                                      */
	/*    http://mathforum.org/dr.math/faq/faq.circle.segment.html#8 */
	radius =  ((diameter * diameter) + (4 * height * height)) / (8 * height);
	fov = 2 * asin(diameter / (2 * radius));
	
	/* Calcaulate the radius value */
	r = MI_SQRT( ( x * x ) + ( y * y ) );

	if ( r < 1.0 ) {

		/* Calculate phi... */
		if ( (r > -EPSILON) && (r < EPSILON) ) {
			phi = 0.0;
		} else {
			phi = atan2(x,y);	// [rz] using atan2 instead of original if-then formula
		}

		/* Calculate theta... */
		theta = r * ( fov / 2.0 );

		/* Calculate Ray direction vector... */
		ray.x = (float)(sin(theta) * cos(phi));
		ray.y = (float)(-sin(theta) * sin(phi));
			/* -Z is Look At Direction*/
		ray.z = (float)(-cos(theta));

		/* Account for view offset... */
		/* Offset is added to y & z components because 
		   they are negative values...*/
		ray.x = ray.x - viewpt_offset.x;
		ray.y = ray.y + viewpt_offset.y; 
			/* Add because MR uses -Z as Look At */
		ray.z = ray.z + viewpt_offset.z;

		// Flip the ray direction about the y-axis
		if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
 			ray.x = (-ray.x);
		}

        	/* Flip the ray direction about the x-axis */
		if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
			ray.y = (-ray.y);		
		}

		/* Convert ray from camera space */
		mi_vector_from_camera(state, &ray, &ray);

		/* Trace new ray... */
		return(mi_trace_eye(result, state, &state->org, &ray));

	} else {

		/* Set return color to Black */
		result->r = result->g =
		result->b = result->a = 0;
		return(miFALSE);
	}

} /* end of domeAFL_WxH() */