/*smoothzoom.c
  Eric Pepke
  Does a smooth zoom from one eye position and focus distance to another

  Usage:
        smoothzoom zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel

  Example:
	smoothzoom 6.0  0 0 -6  2.0 0 1 -2  90 10

  starts off with the eye at (0 0 -6) focusing on a point 6 units away and
  zooms to the eye at (0 1 -2) focusing on a point 2 units away.  It takes
  90 frames (3 seconds) to do this and has an acceleration and a deceleration
  step of 10 frames apiece.

  The zoom distance is the distance from the eye to the focus point, and it
  is changed via the perspective control.  The eye position is changed by the
  perspective control and by moving the space.  The best way to get the 
  numbers is to save to a log while interactively moving.  There will be a
  begin snapshot/end snapshot block, and a value for LOCATION will be in the
  block.
*/

#include <stdio.h>
#include <math.h>

double zoom1, posn1[3];
double zoom2, posn2[3];

void DoFunction(amount, lastAmount)
double amount, lastAmount;
/*Does the function to get to amount (in [0,1]) from lastAmount*/
{
    {
	printf("set value Perspective\\ Control [%lg 25 0.1 8]\n",
		zoom2 * amount + zoom1 * (1.0 - amount));

	printf("eyeposn [%lg %lg %lg]\n",
		posn2[0] * amount + posn1[0] * (1.0 - amount),
		posn2[1] * amount + posn1[1] * (1.0 - amount),
		posn2[2] * amount + posn1[2] * (1.0 - amount)
		);
	printf("snap\n");
    }
}

main(argc, argv)
int argc;
char *argv[];
{
    double minorStep, amount, lastAmount, check;
    long nSteps, nAccel, accelTotal, nInertial, inertialTotal, totalMinorSteps;
    long progress;
    long k;
    if (argc != 11)
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[1], "%lg", &zoom1))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[2], "%lg", &(posn1[0])))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[3], "%lg", &(posn1[1])))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[4], "%lg", &(posn1[2])))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }

    if (1 != sscanf(argv[5], "%lg", &zoom2))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[6], "%lg", &(posn2[0])))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[7], "%lg", &(posn2[1])))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[8], "%lg", &(posn2[2])))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }

    if (1 != sscanf(argv[9], "%d", &nSteps))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[10], "%d", &nAccel))
    {
        fprintf(stderr, "usage: %s zoom1 x1 y1 z1 zoom2 x2 y2 z2 nSteps nAccel\n", argv[0]);
	exit(-1);
    }

    /*Calculate the number of time steps in the inertial phase*/
    nInertial = nSteps - 2 * nAccel;
    if (nInertial < 0)
    {
	fprintf(stderr, "The total number of steps is not be enough for acceleration.\n");
	exit(-1);
    }

    /*Calculate the number of minor steps in each acceleration phase*/
    accelTotal = 0;
    for (k = 1; k <= nAccel; ++k)
    {
	accelTotal += k;
    }

    /*Calculate the number of minor steps in the inertial phase*/
    inertialTotal = nInertial * (nAccel + 1);

    /*Calculate the total number of minor steps*/
    totalMinorSteps = inertialTotal + 2 * accelTotal;

    /*Start off with none done*/
    progress = 0;
    lastAmount = 0;

    /*Do acceleration phase*/
    for (k = 1; k <= nAccel; ++k)
    {
	progress += k;
	lastAmount = amount;
	amount = ((double) progress) / ((double) totalMinorSteps);
	DoFunction(amount, lastAmount);
    }

    /*Do inertial phase*/
    for (k = 0; k < nInertial; ++k)
    {
	progress += nAccel + 1; 
	lastAmount = amount;
	amount = ((double) progress) / ((double) totalMinorSteps);
	DoFunction(amount, lastAmount);
    }

    /*Do deceleration phase*/
    for (k = nAccel; k >= 1; --k)
    {
	progress += k;
	lastAmount = amount;
	amount = ((double) progress) / ((double) totalMinorSteps);
	DoFunction(amount, lastAmount);
    }
}
