/*
 * LorenzAttractorApplet.java
 *
 * (C) 2009, Alex S.
 */

import java.awt.*;
import java.util.*;


public class LorenzAttractorApplet extends BufferedApplet {

    pMatrix matrix = pMatrix.I(4,4);    // model transform
    pMatrix pmatrix = pMatrix.I(4,4);   // screen/perspective
    
    // let user rotate thing
    pMatrix ocamera = pMatrix.toQuaternion(0,0,0);
    
    // list of points.
    pList points = new pList();

    // mouse state
    private int m_mousex,m_mousey;

    /**
     * initialize things
     */
    public void init(){
        super.init();
        m_mousex = m_mousey = 0;
        pmatrix.translate3dEq(bounds().width/2,bounds().height/2,0);
        pmatrix.perspective3d(bounds().width/1.5,-1);
        pmatrix.reflecty3dEq();     // make sure Y axis points up.

        points.push(new double[]{0.1,0,0,1});
        for(int i=0;i<1000;i++){
            double[] p = (double[])points.get(-1);
            points.push(new double[]{            
                p[0] + (0.01 * (10*(p[1] - p[0]))),
                p[1] + (0.01 * (p[0]*(28-p[2])-p[1])),
                p[2] + (0.01 * (p[0]*p[1] - 8.0/3.0*p[2])),
                1
            });
        }
    }

    /**
     * function that gets called whenever something needs to be
     * rendered.
     */
    public void render(Graphics g) {
    
        {
            double[] p = (double[])points.get(-1);
            points.push(new double[]{
                p[0] + (0.01 * (10*(p[1] - p[0]))),
                p[1] + (0.01 * (p[0]*(28-p[2])-p[1])),
                p[2] + (0.01 * (p[0]*p[1] - 8.0/3.0*p[2])),
                1
            });
            points.shift();
        }

        if (damage || true) {

            g.setColor(Color.lightGray);
            g.fillRect(0, 0, bounds().width, bounds().height);

            matrix.push();

            matrix.translate3dEq(0,0,-144);     // move back a bit.
            matrix.scale3dEq(2.6);              // make it bigger.

            matrix.multEq(ocamera.quatToM());   // apply user's mouse rotations.

            matrix.translate3dEq(0,0,-25);      // middle of lorenz is around z=25

            g.setColor(Color.black);
            
            double[] lp = null;
            for(int i=0;i<points.size();i++){
                double[] p = (double[])points.get(i);
                p = matrix.mult(p);
                p = pmatrix.mult(p);                
                p[0] = p[0] / p[3];
                p[1] = p[1] / p[3];
                if(lp != null){
                    g.drawLine((int)lp[0],(int)lp[1],(int)p[0],(int)p[1]);
                }else{
                    g.setColor(Color.red);
                    g.fillOval((int)p[0]-2,(int)p[1]-2,5,5);
                    g.setColor(Color.black);
                }
                g.fillOval((int)p[0]-1,(int)p[1]-1,3,3);
                lp = p;
            }
            g.setColor(Color.blue);
            g.fillOval((int)lp[0]-2,(int)lp[1]-2,5,5);

            g.setColor(Color.black);
            double[] p = (double[])points.get(-1);
            g.drawString("x="+Math.round(p[0])+", y="+Math.round(p[1])+", z="+Math.round(p[2]),10,10);

            matrix.pop();
        }
    }

    /**
     * mouse event handler
     */
    public boolean mouseDown(Event evt, int x, int y){
        m_mousex = x;
        m_mousey = y;
        return true;
    }
    public boolean mouseUp(Event evt, int x, int y){
        return true;
    }

    /**
     * mouse event handler: let the user move things
     */
    public boolean mouseDrag(Event evt, int x, int y){
        ocamera.set(
            pMatrix.toQuaternion(
                0.015 * (y - m_mousey),
                0.015 * (x - m_mousex),
                0
            ).quatMult(ocamera)
        );
        
        m_mousex = x;
        m_mousey = y;

        damage = true;
        return true;
    }   
}


