import javax.media.j3d.*;
import javax.vecmath.*;

import com.sun.j3d.utils.geometry.ColorCube;

public class LocalRotInterpolator
{

        private TransformGroup xRotationGroup;                          // transform group for X rotation
        private TransformGroup yRotationGroup;                          // transform group for Y rotation
        private TransformGroup zRotationGroup;                          // transform group for Z rotation
        private TransformGroup xlatGroup;                                       // transform group for translation to origin
        private TransformGroup invXlatGroup;                            // transform group for translation back to pivot

        private Alpha xAlpha;                                                           // alpha object for X rotation
        private Alpha yAlpha;                                                           // alpha object for Y rotation
        private Alpha zAlpha;                                                           // alpha object for Z rotation
	
        private RotationInterpolator xRotator;                          // interpolator for X rotation
        private RotationInterpolator yRotator;                          // interpolator for Y rotation
        private RotationInterpolator zRotator;                          // interpolator for Z rotation

        private Transform3D xlatTransform;                                      // transform for pivot to origin
        private Transform3D invXlatTransform;                           // transform for origin to pivot
        private Transform3D xRotTransform;                                      // transform for rotation about X axis
        private Transform3D yRotTransform;                                      // transform for rotation about Y axis
        private Transform3D zRotTransform;                                      // transform for rotation about Z axis

        private float curXAngle=0.0f;                                         // current X-axis angle
        private float curYAngle=0.0f;                                         // current Y-axis angle
        private float curZAngle=0.0f;                                         // current Z-axis angle

        // Rotation constraints
        private float maxPosXAngleRot=0.0f;                                   // Maximum angle for positive X axis rotation
        private float maxNegXAngleRot=0.0f;                                   // Maximum angle for negative X axis rotation
        private float maxPosYAngleRot=0.0f;                                   // Maximum angle for positive Y axis rotation
        private float maxNegYAngleRot=0.0f;                                   // Maximum angle for negative Y axis rotation
        private float maxPosZAngleRot=0.0f;                                   // Maximum angle for positive Z axis rotation
        private float maxNegZAngleRot=0.0f;                                   // Maximum angle for negative Z axis rotation
	
        private float boundsRadius=1.0f;                                      // Radius for bounds

        private long defXRotDuration=1000;                                    // Default Duration (in ms, lower is faster) for X axis rotation
        private long defYRotDuration=1000;                                    // Default Duration (in ms, lower is faster) for X axis rotation
        private long defZRotDuration=1000;                                    // Default Duration (in ms, lower is faster) for X axis rotation

        // For use with Rotate()
        public static float angleUnchanged = 4 * (float) Math.PI;
	
        public LocalRotInterpolator(Shape3D Pivot)
        {
                Point3d pivotPoint;
        	
                // Find the pivot point (center of the pivot cube)
                pivotPoint = findPivotPoint(Pivot);
        	
                // Setup the interpolator
                setupLocalRotInterpolator(pivotPoint);
        }
	
        // Finds the center of a pivot cube
        private Point3d findPivotPoint(Shape3D PivotCube)
        {
                GeometryArray geom;
                Point3d pivotPoint;                                                             // Pivot point
                double xCoord=0;                                                              // Average x coordinate
                double yCoord=0;                                                              // Average y coordinate
                double zCoord=0;                                                              // Average z coordinate
                double [] coords;
        	
                // Initialize the dummy coordinate values
                coords = new double[3];
        	
                // Get the geometry of the cube
                geom = (GeometryArray) PivotCube.getGeometry();
	
                // Get the total coordinate values
                for (int i=0;i<geom.getVertexCount();i++)
                {
                        geom.getCoordinate(i,coords);
                        xCoord+=coords[0];
                        yCoord+=coords[1];
                        zCoord+=coords[2];
                }
        	
                // Calculate the average coordinate value
                xCoord/=(double) geom.getVertexCount();
                yCoord/=(double) geom.getVertexCount();
                zCoord/=(double) geom.getVertexCount();
        	
                // Construct the pivot point
                pivotPoint = new Point3d(xCoord,yCoord,zCoord);
        	
                return(pivotPoint);
        }
	
        // Sets up the local rotation interpolator
        private void setupLocalRotInterpolator(Point3d pivotPoint)
        {
                Transform3D transMatrix;                                        // needed for translations
                Vector3d transVec,invTransVec;                          // vectors used for translating

                // Setup the pivot to origin / origin to pivot vectors
                invTransVec = new Vector3d((Tuple3d) pivotPoint);
                pivotPoint.negate();
                transVec = new Vector3d((Tuple3d) pivotPoint);
	
                // Setup the translation transforms and transform groups
                xlatTransform = new Transform3D();
                xlatTransform.setTranslation(transVec);
                xlatGroup = new TransformGroup(xlatTransform);
                invXlatTransform = new Transform3D();
                invXlatTransform.setTranslation(invTransVec);
                invXlatGroup = new TransformGroup(invXlatTransform);  	
        	
                // Initialize the rotation transforms
                xRotTransform = new Transform3D();
                yRotTransform = new Transform3D();
                zRotTransform = new Transform3D();
                xRotTransform.rotZ(Math.PI/2);
                zRotTransform.rotX(Math.PI/2);

                // Note: default matrix for Transform3D() sets rotation about Y-axis

                // Setup the target rotation groups
                xRotationGroup = new TransformGroup();
                yRotationGroup = new TransformGroup();
                zRotationGroup = new TransformGroup();
                xRotationGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                yRotationGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                zRotationGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

                // Create an alpha object - disabled - with default X-axis Duration
                xAlpha = new Alpha(0,defXRotDuration);
                yAlpha = new Alpha(0,defYRotDuration);
                zAlpha = new Alpha(0,defZRotDuration);

                // Create a bounding sphere for the rotations
                BoundingSphere bounds = new BoundingSphere(pivotPoint, boundsRadius);

                // Create the rotation interpolators
                xRotator = new RotationInterpolator(xAlpha,xRotationGroup);
                yRotator = new RotationInterpolator(yAlpha,yRotationGroup);
                zRotator = new RotationInterpolator(zAlpha,zRotationGroup);
                xRotator.setSchedulingBounds(bounds);
                yRotator.setSchedulingBounds(bounds);
                zRotator.setSchedulingBounds(bounds);
                xRotator.setAxisOfRotation(xRotTransform);
                yRotator.setAxisOfRotation(yRotTransform);
                zRotator.setAxisOfRotation(zRotTransform);

                // Disable the rotation interpolators
                xRotator.setEnable(false);
                yRotator.setEnable(false);
                zRotator.setEnable(false);

                // Setup the transform group hierarchy
                zRotationGroup.addChild(xlatGroup);
                yRotationGroup.addChild(zRotationGroup);
                xRotationGroup.addChild(yRotationGroup);
                invXlatGroup.addChild(xRotationGroup);

                // Add the rotation interpolators to the transform group hierarchy
                zRotationGroup.addChild(xRotator);
                zRotationGroup.addChild(yRotator);
                zRotationGroup.addChild(zRotator);
        }

        // Adds a child to the bottom of the transform group hierarchy
        public void addChild(Node object)
        {
                xlatGroup.addChild(object);
        }

        // Returns the transform group object at the top of the hierarchy
        public TransformGroup getTransformGroup()
        {
                return(invXlatGroup);
        }


        // Determines whether the local rotation interpolator is currently rotating
        public boolean isRotating()
        {
                // If any one of the alphas is still rotating, return true
                if ((xAlpha.finished()==false) || (yAlpha.finished()==false) || (zAlpha.finished()==false))
                        return(true);
                else
                        return(false);
        }
        	
        // Rotates the object - default Duration
        public void Rotate(float xAngle, float yAngle, float zAngle)
        {
                Rotate(xAngle,yAngle,zAngle,defXRotDuration,defYRotDuration,defZRotDuration);
        }

        // Rotates the object -- Duration given
        public void Rotate(float xAngle, float yAngle, float zAngle, long xRotDuration, long yRotDuration, long zRotDuration)
        {
                boolean RotateX=false, RotateY=false, RotateZ=false;
        	
                // Check to see if we are currently rotating
                if (isRotating())
                {
                        System.out.println("LocalRotInterpolator error: attempt to perform multiple simulatenous rotations.");
                        return;
                }
                	
                // Setup the x rotation
                if ((xAngle != angleUnchanged) && (xAngle != curXAngle))
                {
                        // Verify that the rotation angle is within bounds - correct it if necessary
                        if (xAngle > maxPosXAngleRot)
                                xAngle = maxPosXAngleRot;
                        else
                                if (xAngle < maxNegXAngleRot)
                                        xAngle = maxNegXAngleRot;

                        // Setup the rotation interpolator
                        xRotator.setMinimumAngle(curXAngle);
                        xRotator.setMaximumAngle(xAngle);
                	
                        // Setup the alpha object
                        xAlpha.setIncreasingAlphaDuration(xRotDuration);

                        // Setup the new current angle
                        curXAngle = xAngle;

                        // Indicate that we will rotate X
                        RotateX=true;
                }

                // Setup the y rotation
                if ((yAngle != angleUnchanged) && (yAngle != curYAngle))
                {
                        // Verify that the rotation angle is within bounds - correct it if necessary
                        if (yAngle > maxPosYAngleRot)
                                yAngle = maxPosYAngleRot;
                        else
                                if (yAngle < maxNegYAngleRot)
                                        yAngle = maxNegYAngleRot;
                	
                        // Setup the rotation interpolator
                        yRotator.setMinimumAngle(curYAngle);
                        yRotator.setMaximumAngle(yAngle);
                	
                        // Setup the alpha object
                        yAlpha.setIncreasingAlphaDuration(yRotDuration);

                        // Setup the new current angle
                        curYAngle = yAngle;
                	
                        // Indicate that we will rotate Y
                        RotateY=true;
                }

                // Setup the z rotation
                if ((zAngle != angleUnchanged) && (zAngle != curZAngle))
                {
                        // Verify that the rotation angle is within bounds - correct it if necessary
                        if (zAngle > maxPosZAngleRot)
                                zAngle = maxPosZAngleRot;
                        else
                                if (zAngle < maxNegZAngleRot)
                                        zAngle = maxNegZAngleRot;
                	
                        // Setup the rotation interpolator
                        zRotator.setMinimumAngle(curZAngle);
                        zRotator.setMaximumAngle(zAngle);
                	
                        // Setup the alpha object
                        zAlpha.setIncreasingAlphaDuration(zRotDuration);

                        // Setup the current angle
                        curZAngle = zAngle;
                	
                        // Indicate that we will rotate X
                        RotateZ=true;
                }

        	
                // Finally ... enable interpolators and alpha objects
                if (RotateX == true)
                {
                        xRotator.setEnable(true);
                        xAlpha.setLoopCount(1);
                }                       	
                if (RotateY == true)
                {
                        yRotator.setEnable(true);
                        yAlpha.setLoopCount(1);
                }                       	
                if (RotateZ == true)
                {
                        zRotator.setEnable(true);
                        zAlpha.setLoopCount(1);
                }                       	
        }

        // Functions for getting and setting rotational constraints

        // Gets the maximum positive X angle rotation
        public float getMaxPosXAngleRot()
        {
                return(maxPosXAngleRot);
        }
	
        // Sets the maximum positive X angle rotation
        public void setMaxPosXAngleRot(float angle)
        {
                maxPosXAngleRot = angle;
        }

        // Gets the maximum negative X angle rotation
        public float getMaxNegXAngleRot()
        {
                return(maxNegXAngleRot);
        }
	
        // Sets the maximum negative X angle rotation
        public void setMaxNegXAngleRot(float angle)
        {
                maxNegXAngleRot = angle;
        }

        // Gets the maximum positive Y angle rotation
        public float getMaxPosYAngleRot()
        {
                return(maxPosYAngleRot);
        }
	
        // Sets the maximum positive Y angle rotation
        public void setMaxPosYAngleRot(float angle)
        {
                maxPosYAngleRot = angle;
        }

        // Gets the maximum negative Y angle rotation
        public float getMaxNegYAngleRot()
        {
                return(maxNegYAngleRot);
        }
	
        // Sets the maximum negative Y angle rotation
        public void setMaxNegYAngleRot(float angle)
        {
                maxNegYAngleRot = angle;
        }
        // Gets the maximum positive Z angle rotation
        public float getMaxPosZAngleRot()
        {
                return(maxPosZAngleRot);
        }
	
        // Sets the maximum positive Z angle rotation
        public void setMaxPosZAngleRot(float angle)
        {
                maxPosZAngleRot = angle;
        }

        // Gets the maximum negative Z angle rotation
        public float getMaxNegZAngleRot()
        {
                return(maxNegZAngleRot);
        }
	
        // Sets the maximum negative Z angle rotation
        public void setMaxNegZAngleRot(float angle)
        {
                maxNegZAngleRot = angle;
        }

        // Gets the current bounding radius
        public float getBoundsRadius()
        {
                return(boundsRadius);
        }
	
        // Sets the current bounding radius
        public void setBoundsRadius(float radius)
        {
                boundsRadius = radius;
        }

        // Gets the default X axis rotation Duration
        public long getDefXRotDuration()
        {
                return(defXRotDuration);
        }
	
        // Sets the default X axis rotation Duration
        public void setDefXRotDuration(long duration)
        {
                defXRotDuration = duration;
        }

                // Gets the default Y axis rotation Duration
        public long getDefYRotDuration()
        {
                return(defYRotDuration);
        }
	
        // Sets the default Y axis rotation Duration
        public void setDefYRotDuration(long duration)
        {
                defYRotDuration = duration;
        }

                // Gets the default Z axis rotation Duration
        public long getDefZRotDuration()
        {
                return(defZRotDuration);
        }
	
        // Sets the default Z axis rotation Duration
        public void setDefZRotDuration(long duration)
        {
                defZRotDuration = duration;
        }

}