import java.awt.*;
import java.awt.event.*;
import java.applet.*;


public class project extends Frame {

    public static void main(String argv[]) {
        HomPoint ballStart = new HomPoint(Integer.parseInt(argv[0]),Integer.parseInt(argv[1]),Integer.parseInt(argv[2]));

        new project(ballStart);
    }

    public project(HomPoint ballStart) {

        this.addWindowListener(new WindowAdapter() {
            public void WindowClosing(WindowEvent e) {close();}
        });

        this.setSize(208,226);
        //this.show();

        BouncingBall bball = new BouncingBall(ballStart);
        this.add(bball);
        Thread bb = new Thread(bball);
        bb.start();
        this.show();
    }

    void close() {
        System.exit(0);
    }
}
    

class BouncingBall extends Component implements Runnable{

    // Cube definition.

    final HomPoint[] cubePoints = { new HomPoint(-100,-100,-100), new HomPoint(100,-100,-100),
                                    new HomPoint(-100,100,-100), new HomPoint(100,100,-100),
                                    new HomPoint(-100,-100,100), new HomPoint(100,-100,100),
                                    new HomPoint(-100,100,100), new HomPoint(100,100,100) };

    final int[][] cubeEdges = { {0,1},{0,2},{2,3},{3,1},
                              {4,0},{5,1},{6,2},{7,3} };

    // Ball pre-definition.

    HomPoint ballCenter = new HomPoint();
    HomPoint[] ballSidePoints;

    final int[][] ballCircs = { {0,4,1,5},{5,3,4,2},{0,3,1,2} };
    int[] ballMotion = {1,1,1};
    int[][] currTranslation = { {1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1} };

    // Projection definition.

    HomPoint prp = new HomPoint(0,0,300);
    float zvp=100;
    float dp=(new Integer(prp.z).floatValue())-zvp;
    float[][] persTransMat = { {1,0,0,0},
                             {0,1,0,0},
                             {0,0,-zvp/dp,zvp*(prp.z/dp)},
                             {0,0,-1/dp,prp.z/dp} };

    // Run method.

    public void run() {
        while (true) {
            try { Thread.sleep(50); } catch (InterruptedException e) {}
            moveBall();
            repaint();
        }
    }

    // Creation methods.

    public BouncingBall(HomPoint ballStart) {
       this.setSize(200,200);

    // Finish ball definition.

    ballCenter = ballStart;

    HomPoint[] calcBallSidePoints = {
        new HomPoint(ballCenter.x-10,ballCenter.y,ballCenter.z),
        new HomPoint(ballCenter.x+10,ballCenter.y,ballCenter.z),
        new HomPoint(ballCenter.x,ballCenter.y-10,ballCenter.z),
        new HomPoint(ballCenter.x,ballCenter.y+10,ballCenter.z),
        new HomPoint(ballCenter.x,ballCenter.y,ballCenter.z-10),
        new HomPoint(ballCenter.x,ballCenter.y,ballCenter.z+10) };

    ballSidePoints = calcBallSidePoints;

    }

    // Screen paint method.

    public void paint(Graphics g){
        drawCube(g);
        drawBall(g);
    }

    // Element drawing functions.
    
    public void drawBall(Graphics g) {
        int circWidth,circHeight,circX,circY;

        HomPoint[] pBall = perspectiveTransform(ballSidePoints);
        HomPoint[] pvBall = toViewCoords(pBall);
        for (int i=0;i<3;i++) {

            circX=pvBall[ballCircs[i][0]].x;
            circY=pvBall[ballCircs[i][1]].y;
            circWidth=(pvBall[ballCircs[i][2]].x-pvBall[ballCircs[i][0]].x);
            circHeight=(pvBall[ballCircs[i][3]].y-pvBall[ballCircs[i][1]].y);

            if (circWidth<0) {
                circWidth = Math.abs(circWidth);
                circX -= circWidth;
            }
            if (circHeight<0) {
                circHeight = Math.abs(circHeight);
                circY -= circHeight;
            }

            g.drawOval(circX,circY,circWidth,circHeight);
        }
    }
    

    public void moveBall() {
        int[][] newTranslation = { {1,0,0,ballMotion[0]},
                                   {0,1,0,ballMotion[1]},
                                   {0,0,1,ballMotion[2]},
                                   {0,0,0,1} };

        ballCenter.x+=ballMotion[0];
        ballCenter.y+=ballMotion[1];
        ballCenter.z+=ballMotion[2];

        int[] ballCenterVect = ballCenter.toVect();

        //System.out.println(ballCenterVect[0]+" "+ballCenterVect[1]+" "+ballCenterVect[2]);

        for (int i=0;i<3;i++) {
            if (ballCenterVect[i] > 90 || ballCenterVect[i] < -90) {
                ballMotion[i] = -ballMotion[i];
        //        System.out.println("ballMotion["+i+"] reversed");
            }
        }

        currTranslation = newTranslation;
        ballSidePoints = translate(ballSidePoints);
    }

    private void drawCube(Graphics g) {
        HomPoint[] pCube = perspectiveTransform(cubePoints);
        HomPoint[] pvCube = toViewCoords(pCube);
        for (int i=0;i<8;i++) {
            g.drawLine(pvCube[cubeEdges[i][0]].x,pvCube[cubeEdges[i][0]].y,pvCube[cubeEdges[i][1]].x,pvCube[cubeEdges[i][1]].y);
        }
    }

    // Transformation functions.

    private HomPoint[] perspectiveTransform(HomPoint[] a) {
        HomPoint[] retPts = new HomPoint[a.length];
        for (int i=0;i<a.length;i++){
            retPts[i] = perspectiveTransform(a[i]);
        }
        return retPts;
    }

    private HomPoint perspectiveTransform(HomPoint a) {
        int[] vect = a.toVect();
        int[] newvect = matVectMult(vect,persTransMat);
        HomPoint retPoint = new HomPoint(newvect);
        float h=(prp.z-a.z)/dp;
        retPoint.x=(int) (new Integer(retPoint.x).floatValue()/h);
        retPoint.y=(int) (new Integer(retPoint.y).floatValue()/h);
        return retPoint;
    }

    private HomPoint[] translate(HomPoint[] a) {
        HomPoint[] retPts = new HomPoint[a.length];
        for (int i=0;i<a.length;i++) {
            retPts[i] = translate(a[i]);
        }
        return retPts;
    }

    private HomPoint translate(HomPoint a) {
        int[] vect = a.toVect();
        int[] newvect = matVectMult(vect,currTranslation);
        HomPoint retPoint = new HomPoint(newvect);
        return retPoint;
    }

    private HomPoint[] toViewCoords(HomPoint[] a) {
        HomPoint[] rpa = new HomPoint[a.length];
        for (int i=0;i<a.length;i++) {
            rpa[i] = toViewCoords(a[i]);
        }
        return rpa;
    }

    private HomPoint toViewCoords(HomPoint a) {
        HomPoint rp = new HomPoint(100+a.x,100+a.y,a.z);
        return rp;
    }

    // Mathematic functions.

    private int[] matVectMult(int[] a, int[][] M) {
        int[] c = {0,0,0,0};
        for (int i=0;i<4;i++){
            for (int k=0;k<4;k++){
                c[i]+=M[i][k]*a[k];
        }   }
        return c;
    }

    private int[] matVectMult(int[] a, float[][] M) {
        int[] c = {0,0,0,0};
        for (int i=0;i<4;i++){
            for (int k=0;k<4;k++){
                c[i]+=(int) (M[i][k]*(new Integer(a[k]).floatValue()));
        }   }
        return c;
    }
        
}


// A class to encapsulate homogeneous 3D points.

class HomPoint {
    int x;
    int y;
    int z;
    int h=1;

    // Constructors.

    public HomPoint() {
        x=y=z=0;
    }

    public HomPoint(int x, int y, int z) {
        this.x=x;
        this.y=y;
        this.z=z;
    }

    public HomPoint(int[] a) {
        this.x=a[0];
        this.y=a[1];
        this.z=a[2];
        this.h=a[3];
    }

    // Convertor.

    public int[] toVect() {
        int[] rv = new int[4];
        rv[0]=this.x;
        rv[1]=this.y;
        rv[2]=this.z;
        rv[3]=this.h;
        return rv;
    }

}  

