/*smoothrots.c
  Eric Pepke

  Emits a script segment containing a series of rotations about an axis 
  and snaps to standard output, with acceleration at the beginning and
  deceleration at the end

  Usage:
    rots axis step nSteps

  Example:
    rots z 90.0 115 10 > rot115

  rotates about the z axis a total of 90 degrees in 115 steps with start
  and stop accelerations of 10 steps each, placing the result in file rot115
*/

#include <stdio.h>

char axis;
double total;

void DoFunction(amount, lastAmount)
double amount, lastAmount;
/*Does the function to get to amount (in [0,1]) from lastAmount*/
{
    {
	printf("rotate %c %lg\n", axis, (amount - lastAmount) * total);
	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 != 5)
    {
        fprintf(stderr, "usage: %s axis total nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[1], "%c", &axis))
    {
        fprintf(stderr, "usage: %s axis total nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[2], "%lg", &total))
    {
        fprintf(stderr, "usage: %s axis total nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[3], "%d", &nSteps))
    {
        fprintf(stderr, "usage: %s axis total nSteps nAccel\n", argv[0]);
	exit(-1);
    }
    if (1 != sscanf(argv[4], "%d", &nAccel))
    {
        fprintf(stderr, "usage: %s axis total 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);
    }
}
