
package Graphics;

import java.awt.*;
import java.lang.*;
import java.util.*;
import java.awt.image.*;



public class Object
{
  private static final int MAX_POLYGONS = 8192;
  private Vector colours[] = new Vector[MAX_POLYGONS];
  private Vector current_colour,orig_colour;
  private MyPolygon polygons[] = new MyPolygon[MAX_POLYGONS];
  private int nr_polygons = 0;

  private boolean is_a_sphere = false;
  private Vector center;
  private double radius;
  private double height;
  public int wireframe=6;

  public Vector objcenter=null;
  public Vector auto_objcenter=new Vector(0,0,0);

  Light light=null;

  Random m=new Random();


  // animation parameters
  Vector[] path=new Vector[10];
  int npaths=0;
  Matrix r_transform=new Matrix();
  Matrix r_temp_transform=new Matrix();
  Matrix anim_transform=new Matrix();


  Vector[] rlocations=new Vector[10];
  int[] rtype=new int[10];
  double[] rangles=new double[10];

  boolean rendered=false;


  //____for terrain
  double rfactor=0;

   public void setLight(Vector pos , Vector col)
   {
    light=new Light(pos,col);
    objcenter=new Vector(pos);
    
   }
   

  public void set_anim(int frameno, int totalframes)
  {
    //__Set translation

    
    Vector trans=new Vector();
    if(npaths>1)
    {

    int fp=totalframes/(npaths-1);

    int i=frameno/fp;    //___ determine current path no
    int j,k;

    if(i>=npaths)
      i=npaths-1;

    trans=path[i].vclone();
    trans=trans.subtract(path[0]);

    Vector sub=new Vector();
    if(i+1<npaths)
    {
       sub=path[i+1].subtract(path[i]);
    }

    double ff=((double)(frameno-i*fp))/fp;
    

    if(ff>1.0)
      ff=1.0;

    sub=sub.multiplied_by(ff);

    trans=trans.add(sub);



    }
    //add current path

     r_temp_transform=r_transform.matrix_multiply(r_temp_transform);

      if(frameno==1)
      r_temp_transform.set(r_transform);


      //___We are doing translation with rotatary path here

    Vector rtrans=r_temp_transform.vec_postmultiply(return_center());
    //System.out.println(rtrans);
    rtrans=rtrans.subtract(return_center());
   // System.out.println(rtrans);

     Matrix rtrans_matrix=new Matrix();
    rtrans_matrix.translate(rtrans);


    //____multiply path translation
    anim_transform.translate(trans);

    // ___ multiply rotation translation
    anim_transform=rtrans_matrix.matrix_multiply(anim_transform);

   // if(light!=null)
    //  anim_transform.display();

  }

  public void rotate_x(Vector location,double angle)
  {

    Matrix t=new Matrix();
    Matrix m=new Matrix();
      Matrix r=new Matrix();



      
    m.rotate_x(angle);
    r=new Matrix(m);



    t=m.matrix_multiply(t);

    t.rotate_xl(location,angle);
    
     int i=0;
     for(i=0;i<nr_polygons;i++)
     {
      polygons[i].rotate_x(t,r);
     }

     if(objcenter!=null)
        objcenter=t.vec_postmultiply(objcenter);
     if(auto_objcenter!=null)
        auto_objcenter=t.vec_postmultiply(auto_objcenter);



  }

   public void rotate_y(Vector location,double angle)
  {

   Matrix t=new Matrix();
    Matrix m=new Matrix();

    Matrix r=new Matrix();


    m.rotate_y(angle);
     r=new Matrix(m); // for normal



    t.rotate_yl(location,angle);
     int i=0;
     for(i=0;i<nr_polygons;i++)
     {
      polygons[i].rotate_y(t,r);
     }

     if(objcenter!=null)
        objcenter=t.vec_postmultiply(objcenter);
     if(auto_objcenter!=null)
        auto_objcenter=t.vec_postmultiply(auto_objcenter);



  }

   public void rotate_z(Vector location,double angle)
  {

   Matrix t=new Matrix();
    Matrix m=new Matrix();
      Matrix r=new Matrix();

    m.rotate_z(angle);
    r=new Matrix(m);
   t.rotate_zl(location,angle);
    
     int i=0;
     for(i=0;i<nr_polygons;i++)
     {
      polygons[i].rotate_z(t,r);
     }

     if(objcenter!=null)
        objcenter=t.vec_postmultiply(objcenter);
     if(auto_objcenter!=null)
        auto_objcenter=t.vec_postmultiply(auto_objcenter);

  }

   public void translate(Vector tr)
  {

   Matrix t=new Matrix();
   t.translate(tr);



     int i=0;
     for(i=0;i<nr_polygons;i++)
     {
      polygons[i].translate(t);
     }

     if(objcenter!=null)
        objcenter=t.vec_postmultiply(objcenter);
     if(auto_objcenter!=null)
        auto_objcenter=t.vec_postmultiply(auto_objcenter);

  }


public  void reset()
  {
    if(objcenter!=null) objcenter.set(0,0,0);
    auto_objcenter.set(0,0,0);
    nr_polygons=0;
    
  }

  public Vector return_center()
  {
      if(objcenter==null)
        return auto_objcenter.divided_by(nr_polygons);

      return objcenter;

  }

  
  public void set_objcenter(Vector new_c)
  {
      objcenter=new_c.vclone();

  }




  public Object()
  {
    current_colour = new Vector(1, 1, 1);
    r_transform.identity();

  }  // constructor




  public void set_sphere(Vector new_center, double new_radius)
  {
    is_a_sphere = true;
    radius = new_radius;
    center = new_center;
    create_sphere(center,radius,current_colour);
    
    colours[0] = new Vector(current_colour);
  }  // set_sphere


  public boolean is_sphere()
  {
    return is_a_sphere;
  }  // is_sphere



  public Vector get_center()
  {
    return center;
  }  // get_center



  public void set_center(Vector new_center)
  {
    center = new_center;
  }  // set_center

   public void set_wireframe(int wf)
  {
    wireframe=wf;
  }  // set_wireframe

  public double get_radius()
  {
    return radius;
  }  // get_radius


  //________Intersection for sphere__________
  public boolean intersect(Vector from, Vector to, Vector intersection)
  {
    double x1 = from.get_x();
    double x2 = to.get_x();
    double y1 = from.get_y();
    double y2 = to.get_y();
    double z1 = from.get_z();
    double z2 = to.get_z();

    double l = center.get_x();
    double m = center.get_y();
    double n = center.get_z();

    double i = x2-x1;
    double j = y2-y1;
    double k = z2-z1;

    double a = i*i + j*j + k*k;
    double b = 2*i*(x1 - l) + 2*j*(y1 - m) + 2*k*(z1 - n);
    double c = l*l + m*m + n*n + x1*x1 + y1*y1 + z1*z1 +
      2*(-l*x1 - m*y1 - n*z1) - radius*radius;

    double det = b*b-4*a*c;

    if (det <= 0) return false;

    double t1 = (-b + Math.sqrt(det))/(2*a);
    double t2 = (-b - Math.sqrt(det))/(2*a);

    if (t1 < t2)
      intersection.set(x1 + i*t1, y1 + j*t1, z1 + k*t1);
    else
      intersection.set(x1 + i*t2, y1 + j*t2, z1 + k*t2);

    return true;
  }  // intersect





  public void add_polygon(MyPolygon p)
  {
    polygons[nr_polygons] = p;
    colours[nr_polygons] = new Vector(current_colour);
    auto_objcenter=auto_objcenter.add(polygons[nr_polygons].return_center());

    nr_polygons++;


  }  // add_polygon

  public void add_polygon(MyPolygon p,Vector color)
  {
    polygons[nr_polygons] = p;
    colours[nr_polygons] = color;
    auto_objcenter=auto_objcenter.add(polygons[nr_polygons].return_center());
    nr_polygons++;
  }  // add_polygon



  public int get_nr_polygons()
  {
    return nr_polygons;
  }  // get_nr_polygons



  public MyPolygon get_polygon(int index)
  {
    return polygons[index];
  }  // get_polygon




  public void set_colour(Vector new_colour)
  {
    current_colour = new_colour;
    colours[0]=current_colour;

            int i=0;
        if(is_a_sphere)
          for(i=0;i<nr_polygons;i++)
            colours[i]=current_colour;
  }  // set_colour
  
  

  public void set_Color(Color new_colour)
  {
    current_colour = new Vector((double) new_colour.getRed() / 255.0D,
				(double) new_colour.getGreen() / 255.0D,
				(double) new_colour.getBlue() / 255.0D);

           colours[0]=current_colour;

        int i=0;
        if(is_a_sphere)
          for(i=0;i<nr_polygons;i++)
            colours[i]=current_colour;



  }  // set_Color



  public Vector get_colour(int index)
  {
    return colours[index];
  }  // get_colour



  public Color get_Color(int index)
  {
    return new Color((float) colours[index].get_x(),
		     (float) colours[index].get_y(),
		     (float) colours[index].get_z());
  }  // get_Color


  public void create_superellipse(Vector center,double a,double b ,double height,double n,double s1,Vector color)
  {


    int segments=wireframe;
    int cpoints=wireframe;

    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;
    for(i=0;i<segments;i++)
    {



      double angle=(Math.PI/2)*i/segments;

      double h=height*Math.pow(Math.sin(angle),s1);
      double a1=a*Math.pow(Math.cos(angle),s1);
      double b1=b*Math.pow(Math.cos(angle),s1);



      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;
   //     double x=a1*Math.pow(Math.cos(cangle),n);
   //     double z=-b1*Math.pow(Math.sin(cangle),n);

        
        double x=a1*Math.cos(cangle)*Math.abs(Math.pow(Math.cos(cangle),n-1));
        double z=-b1*Math.sin(cangle)*Math.abs(Math.pow(Math.sin(cangle),n-1));

        //___add this point to grid
        grid[turn][j]=new Vector(x,h,z);


      } // for j

     /* for(j=0;j<cpoints/4;j++)
      {

        //___add this point to grid
        grid[turn][j+cpoints/4]=new Vector(-grid[turn][cpoints/4-j-1].get_x(),h,grid[turn][cpoints/4-j-1].get_z());
        grid[turn][j+2*cpoints/4]=new Vector(-grid[turn][j].get_x(),h,-grid[turn][j].get_z());
        grid[turn][j+3*cpoints/4]=new Vector(grid[turn][cpoints/4-j-1].get_x(),h,-grid[turn][cpoints/4-j-1].get_z());
      } // for j    */



        if(i>0)
        {
          int t=1-turn;
          for(j=0;j<cpoints;j++)
          {
             //_____add polygons to object  ( triangles )


            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));

           this.add_polygon(p,color);

                p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
               p.addPoint(center.add(grid[turn][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][j]));

           this.add_polygon(p,color);


            p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints].neg_y()));
            p.addPoint(center.add(grid[t][j].neg_y()));
            p.addPoint(center.add(grid[turn][j].neg_y()));
            this.add_polygon(p,color);

              p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints].neg_y()));
            p.addPoint(center.add(grid[turn][j].neg_y()));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints].neg_y()));
            this.add_polygon(p,color);



          }
        }

        turn=1-turn;

      }         // for i

      //_________add apex polygons
      Vector apex=new Vector(center.add(new Vector(0,height,0)));
      Vector apex1=new Vector(center.add(new Vector(0,-height,0)));

         for(j=0;j<cpoints;j++)
         {
            int t=1-turn;

             //_____add polygons to object
            MyPolygon p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(apex);
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);

              //_____add polygons to object
            p = new MyPolygon();
            p.addPoint(center.add(grid[t][j].neg_y()));
            p.addPoint(apex1);
            p.addPoint(center.add(grid[t][(j+1)%cpoints].neg_y()));
            this.add_polygon(p,color);


          }

          objcenter=center.vclone();


    }    // superellipse


    public void create_sphere(Vector center,double radius,Vector color)
  {


    int segments=wireframe;
    int cpoints=wireframe;

    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;
    for(i=0;i<segments;i++)
    {



      double angle=(Math.PI/2)*i/segments;
      double h=radius*Math.sin(angle);
      double r=radius*Math.cos(angle);



      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;
        double x=r*Math.cos(cangle);
        double z=-r*Math.sin(cangle);

        //___add this point to grid
        grid[turn][j]=new Vector(x,h,z);


      } // for j

        if(i>0)
        {
          int t=1-turn;
          for(j=0;j<cpoints;j++)
          {
             //_____add polygons to object  ( triangles )


            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));

           this.add_polygon(p,color);

                p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
               p.addPoint(center.add(grid[turn][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][j]));

           this.add_polygon(p,color);


            p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints].neg_y()));
            p.addPoint(center.add(grid[t][j].neg_y()));
            p.addPoint(center.add(grid[turn][j].neg_y()));
            this.add_polygon(p,color);

              p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints].neg_y()));
            p.addPoint(center.add(grid[turn][j].neg_y()));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints].neg_y()));
            this.add_polygon(p,color);



          }
        }

        turn=1-turn;

      }         // for i

      //_________add apex polygons
      Vector apex=new Vector(center.add(new Vector(0,radius,0)));
      Vector apex1=new Vector(center.add(new Vector(0,-radius,0)));

         for(j=0;j<cpoints;j++)
         {
            int t=1-turn;

             //_____add polygons to object
            MyPolygon p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(apex);
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);

              //_____add polygons to object
            p = new MyPolygon();
            p.addPoint(center.add(grid[t][j].neg_y()));
            p.addPoint(apex1);
            p.addPoint(center.add(grid[t][(j+1)%cpoints].neg_y()));
            this.add_polygon(p,color);


          }

          objcenter=center.vclone();


    }    // sphere

  public void create_donut(Vector center,double radius,double r2,double n,Vector color)
  {


    int segments=wireframe;
    int cpoints=wireframe;

    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;

     

     Vector trans=new Vector(r2,0,0);
     
    for(i=0;i<=segments;i++)
    {

      double angle=(2*Math.PI)*i/segments;
      Matrix r=new Matrix();
      r.rotate_y(angle);

      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;


        double x=radius*Math.cos(cangle)*Math.abs(Math.pow(Math.cos(cangle),n-1));
        double y=radius*Math.sin(cangle)*Math.abs(Math.pow(Math.sin(cangle),n-1));


        //___add this point to grid
        grid[turn][j]=trans.add(new Vector(x,y,0));

         grid[turn][j]=r.vec_postmultiply( grid[turn][j]);
        //___now rotate this point




      } // for j




        if(i>0)
        {
          int t=1-turn;
          for(j=0;j<cpoints;j++)
          {
             //_____add polygons to object  ( triangles )


            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));

           this.add_polygon(p,color);

                p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
               p.addPoint(center.add(grid[turn][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][j]));

           this.add_polygon(p,color);


          



          }
        }

        turn=1-turn;

      }         // for i

     

          objcenter=center.vclone();


    }    // donut

     public void create_pyramid(Vector center,double bt,double lt,double ht,Vector color)
  {


    int segments=wireframe;
    int cpoints=4;
    
    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;
    for(i=0;i<segments;i++)
    {

      double h=ht*i/segments;
      double b=bt*(segments-i)/segments;
       double l=lt*(segments-i)/segments;


      grid[turn][0]=new Vector(b/2,h,l/2);
      grid[turn][1]=new Vector(b/2,h,-l/2);
      grid[turn][2]=new Vector(-b/2,h,-l/2);
      grid[turn][3]=new Vector(-b/2,h,l/2);


        if(i>0)
        {
          int t=1-turn;
          for(j=0;j<4;j++)
          {
             //_____add polygons to object


            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));

           this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][j]));

           this.add_polygon(p,color);


          }
        }

        turn=1-turn;

      }         // for i

      //_________add apex polygons
      Vector apex=new Vector(center.add(new Vector(0,ht,0)));


         for(j=0;j<4;j++)
         {
            int t=1-turn;

             //_____add polygons to object
            MyPolygon p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(apex);
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);

          }



          objcenter=center.add(new Vector(0,ht/2,0)).vclone();


          System.out.println("Creating base");
          
          turn=0;
          int t=0;
          //_____Now create base____

          double ox,oz,bb,bl;
          bb=bt/wireframe;
          bl=lt/wireframe;
          oz=-lt/2;

          for(i=0;i<wireframe;i++)
          {
             ox=-bt/2;
            for(k=0;k<wireframe;k++)
            {
            
              grid[turn][0]=new Vector(ox,0,oz);
               grid[turn][1]=new Vector(ox+bb,0,oz);
               grid[turn][2]=new Vector(ox+bb,0,oz+bl);
               grid[turn][3]=new Vector(ox,0,oz+bl);

            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][0]));
            p.addPoint(center.add(grid[t][1]));
            p.addPoint(center.add(grid[turn][2]));

           this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(center.add(grid[t][0]));
            p.addPoint(center.add(grid[turn][2]));
            p.addPoint(center.add(grid[turn][3]));

           this.add_polygon(p,color);
            ox+=bb;

         }
         oz+=bl;
      }


    } // create pyramid

   public void create_box(Vector center,double bt,double lt,double ht,Vector color)
  {


    int segments=wireframe;
    int cpoints=4;
    
    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;
    for(i=0;i<=segments;i++)
    {

      double h=ht*i/segments;
      double b=bt;
       double l=lt;


      grid[turn][0]=new Vector(b/2,h,l/2);
      grid[turn][1]=new Vector(b/2,h,-l/2);
      grid[turn][2]=new Vector(-b/2,h,-l/2);
      grid[turn][3]=new Vector(-b/2,h,l/2);


        if(i>0)
        {
          int t=1-turn;
          for(j=0;j<4;j++)
          {
             //_____add polygons to object


            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));

           this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(center.add(grid[t][j]));
            p.addPoint(center.add(grid[turn][(j+1)%cpoints]));
            p.addPoint(center.add(grid[turn][j]));

           this.add_polygon(p,color);


          }
        }

        turn=1-turn;

      }         // for i




          objcenter=center.add(new Vector(0,0,0)).vclone();


          System.out.println("Creating base");

          turn=0;
          int t=0;
          //_____Now create base and top____
          Vector apex=new Vector(0,ht,0);
          

          double ox,oz,bb,bl;
          bb=bt/wireframe;
          bl=lt/wireframe;
          oz=-lt/2;

          for(i=0;i<wireframe;i++)
          {
             ox=-bt/2;
            for(k=0;k<wireframe;k++)
            {

              grid[turn][0]=new Vector(ox,0,oz);
               grid[turn][1]=new Vector(ox+bb,0,oz);
               grid[turn][2]=new Vector(ox+bb,0,oz+bl);
               grid[turn][3]=new Vector(ox,0,oz+bl);

            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][0]));
            p.addPoint(center.add(grid[t][1]));
            p.addPoint(center.add(grid[turn][2]));

           this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(center.add(grid[t][0]));
            p.addPoint(center.add(grid[turn][2]));
            p.addPoint(center.add(grid[turn][3]));

           this.add_polygon(p,color);

           // upper part
          p = new MyPolygon();

            p.addPoint(apex.add(center.add(grid[t][0])));
            p.addPoint(apex.add(center.add(grid[t][2])));
            p.addPoint(apex.add(center.add(grid[turn][1])));

           this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(apex.add(center.add(grid[t][0])));
            p.addPoint(apex.add(center.add(grid[turn][3])));
            p.addPoint(apex.add(center.add(grid[turn][2])));

           this.add_polygon(p,color);





            ox+=bb;

         }
         oz+=bl;
      }


    } // create box


    private void make_rocky(Vector points[][],int x1,int x2,int y1,int y2)
    {
        if(x2-x1<=1 && y2-y1<=1)
          return;


         // System.out.println("Called with : " + x1 + " , " + x2 + " , " +  y1 + " , " + y2);

         // System.out.flush();
          
          Random r=new Random();

          points[y1][(x1+x2)/2]=new Vector((points[y1][x1].get_x()+points[y1][x2].get_x())/2,
        (points[y1][x1].get_y()+points[y1][x2].get_y())/2+
        rfactor*r.nextGaussian()*Math.abs(points[y1][x1].get_x()-points[y1][x2].get_x()) ,
        (points[y1][x1].get_z()+points[y1][x2].get_z())/2);

     //   System.out.println(  points[y1][(x1+x2)/2]);


         
          points[y2][(x1+x2)/2]=new Vector((points[y2][x1].get_x()+points[y2][x2].get_x())/2,
        (points[y2][x1].get_y()+points[y2][x2].get_y())/2+
        rfactor*r.nextGaussian()*Math.abs(points[y2][x1].get_x()-points[y2][x2].get_x()) ,
        (points[y2][x1].get_z()+points[y2][x2].get_z())/2);

       // System.out.println(  points[y2][(x1+x2)/2]);


          points[(y1+y2)/2][x1]=new Vector((points[y1][x1].get_x()+points[y2][x1].get_x())/2,
        (points[y1][x1].get_y()+points[y2][x1].get_y())/2+
        rfactor*r.nextGaussian()*Math.abs(points[y1][x1].get_x()-points[y1][x2].get_x()),
        (points[y1][x1].get_z()+points[y2][x1].get_z())/2);

         // System.out.println(points[(y1+y2)/2][x1]);

            points[(y1+y2)/2][x2]=new Vector((points[y1][x2].get_x()+points[y2][x2].get_x())/2,
        (points[y1][x2].get_y()+points[y2][x2].get_y())/2+
        rfactor*r.nextGaussian()*Math.abs(points[y1][x2].get_x()-points[y1][x2].get_x()),
        (points[y1][x2].get_z()+points[y2][x2].get_z())/2);

         // System.out.println(points[(y1+y2)/2][x2]);



        //__Mid point


           points[(y1+y2)/2][(x1+x2)/2]=new Vector((points[y1][x1].get_x()+points[y1][x2].get_x())/2,
        (points[y1][x2].get_y()+points[y2][x2].get_y()+points[y1][x1].get_y()+points[y1][x2].get_y())/4+
        rfactor*r.nextGaussian()*Math.abs(points[y1][x1].get_x()-points[y1][x2].get_x()),
        (points[y1][x1].get_z()+points[y2][x1].get_z())/2);

        // System.out.println( points[(y1+y2)/2][(x1+x2)/2]);

        make_rocky(points,x1,(x1+x2)/2,y1,(y1+y2)/2);
         make_rocky(points,(x1+x2)/2,x2,y1,(y1+y2)/2);
          make_rocky(points,x1,(x1+x2)/2,(y1+y2)/2,y2);
           make_rocky(points,(x1+x2)/2,x2,(y1+y2)/2,y2);

           



    }


 public void create_terrain(double ox ,double  oz,double bt,double lt,double ht,Vector color)
  {
    

    int segments=wireframe;
    int cpoints=4;
    

    Vector points[][]=new Vector[wireframe+1][wireframe+1];

    int i,j,k;
    int turn=0;

          turn=0;
          int t=0;
          //_____Now create base and top____
          Vector apex=new Vector(0,ht,0);


          double bb,bl;
          bb=bt/wireframe;
          bl=lt/wireframe;


          for(i=0;i<=wireframe;i++)
          {
            for(j=0;j<=wireframe;j++)
            {

              points[i][j]=new Vector(ox+bb*j,0,oz+bl*i);

            }
          }

          points[0][0]=new Vector(ox,0,oz);
           points[0][wireframe]=new Vector(ox+bt,0,oz);
            points[wireframe][0]=new Vector(ox,0,oz+lt);
             points[wireframe][wireframe]=new Vector(ox+bt,0,oz+lt);


             rfactor=ht;
          if(rfactor>0)
            make_rocky(points,0,wireframe,0,wireframe);
          
          for(i=0;i<wireframe;i++)
          {
            for(k=0;k<wireframe;k++)
            {


            MyPolygon p = new MyPolygon();

            p.addPoint(points[i][k]);
            p.addPoint(points[i+1][k+1]);
            p.addPoint(points[i][k+1]);


            this.add_polygon(p,color);

            p = new MyPolygon();

            p.addPoint(points[i][k]);
            p.addPoint(points[i+1][k]);
            p.addPoint(points[i+1][k+1]);

           this.add_polygon(p,color);

            p = new MyPolygon();

            p.addPoint(points[i+1][k+1]);
            p.addPoint(points[i+1][k]);
            p.addPoint(points[i][k]);

           this.add_polygon(p,color);


               p = new MyPolygon();
               p.addPoint(points[i][k+1]);
            p.addPoint(points[i+1][k+1]);
            p.addPoint(points[i][k]);

            this.add_polygon(p,color);


         }

      }

      objcenter=new Vector(ox,-5000,oz);
      


    } // create terrain


    private Color handlesinglepixel(int x,int y,int w,int[] p)
    {
      int alpha=(p[y*w+x] >> 24 ) & 0xff;
        int red=(p[y*w+x] >> 16 ) & 0xff;
          int green=(p[y*w+x] >> 8 ) & 0xff;
            int blue=(p[y*w+x] >> 0 ) & 0xff;
            return new Color(red,green,blue);
      }
      



    public void create_mapterrain(double ox ,double  oz,double bt,double lt,double ht,String im,Vector color)
  {
    

    int segments=wireframe;
    int cpoints=4;


       System.out.println("Image : " + im);
       System.out.flush();

          Image image=Toolkit.getDefaultToolkit().getImage(im);



         // Toolkit.getDefaultToolkit().prepareImage(image,-1,-1,null);
         ImgObs is = new ImgObs();
        int w=image.getWidth(is);
       int  h=image.getHeight(is);
       if(w<=0 || h<=0)
        while(!is.avail)
          Thread.yield();

        h=is.height;
        w=is.width;
           int[] pixels = new int[w*h];
           PixelGrabber pg=new PixelGrabber(image,0,0,w,h,pixels,0,w);
           try
           {
            pg.grabPixels();
            }
            catch(InterruptedException e)
            {

            }




          int min=w;
          if(h<min)
            min=h;

            wireframe=min;


            System.out.println("wireframe : " + w+ " " + h + " " + wireframe);


    Vector points[][]=new Vector[wireframe+1][wireframe+1];

    int i,j,k;
    int turn=0;

          turn=0;
          int t=0;
          //_____Now create base and top____
          Vector apex=new Vector(0,ht,0);



          double bb,bl;
          bb=bt/wireframe;
          bl=lt/wireframe;


          for(i=0;i<=wireframe;i++)
          {
            for(j=0;j<=wireframe;j++)
            {
              points[i][j]=new Vector(ox+bb*j,0,oz+bl*i);
            }
          }

          points[0][0]=new Vector(ox,0,oz);
           points[0][wireframe]=new Vector(ox+bt,0,oz);
            points[wireframe][0]=new Vector(ox,0,oz+lt);
             points[wireframe][wireframe]=new Vector(ox+bt,0,oz+lt);


             rfactor=ht;

          if(rfactor>0)
            make_rocky(points,0,wireframe,0,wireframe);

          for(i=0;i<wireframe;i++)
          {
            for(k=0;k<wireframe;k++)
            {

            Color new_colour= handlesinglepixel(k,i,w,pixels);

            color = new Vector((double) new_colour.getRed() / 255.0D,
				(double) new_colour.getGreen() / 255.0D,
				(double) new_colour.getBlue() / 255.0D);


            MyPolygon p = new MyPolygon();

            p.addPoint(points[i][k]);
            p.addPoint(points[i+1][k+1]);
            p.addPoint(points[i][k+1]);


            this.add_polygon(p,color);

            p = new MyPolygon();

            p.addPoint(points[i][k]);
            p.addPoint(points[i+1][k]);
            p.addPoint(points[i+1][k+1]);


           this.add_polygon(p,color);

            p = new MyPolygon();

            p.addPoint(points[i+1][k+1]);
            p.addPoint(points[i+1][k]);
            p.addPoint(points[i][k]);

           this.add_polygon(p,color);


               p = new MyPolygon();
               p.addPoint(points[i][k+1]);
            p.addPoint(points[i+1][k+1]);
            p.addPoint(points[i][k]);

            this.add_polygon(p,color);


         }

      }


    } // create mapterrain

    public void create_cone(Vector center,double radius,double height,Vector color)
    {


    int cpoints=wireframe;

    Vector grid[][]=new Vector[2][cpoints];



    int i,j,k;
    int turn=0;

      double h=0;
      double r=radius*(height-h)/height;



      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;
        double x=r*Math.cos(cangle);
        double z=-r*Math.sin(cangle);

        //___add this point to grid
        grid[turn][j]=new Vector(x,h,z);


      } // for j

            int t=turn;
      //_________add apex polygons
      Vector apex=new Vector(center.add(new Vector(0,height,0)));


         for(j=0;j<cpoints;j++)
         {


             //_____add polygons to object
            MyPolygon p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(apex);
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);


          }

          create_circle(center,radius,color,false);

          Vector apex1=new Vector(0,height/2,0);
        objcenter=center.add(apex1).vclone();
    }   // end cone


    public void create_circle(Vector center,double radius,Vector color,boolean up)
    {



    int cpoints=wireframe;
    
    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;

      double h=0;
      double r=radius;



      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;
        double x=r*Math.cos(cangle);
        double z=-r*Math.sin(cangle);

        //___add this point to grid
        grid[turn][j]=new Vector(x,h,z);


      } // for j


            int t=turn;
      //_________add apex polygons
      Vector apex=center;


      MyPolygon p;
         for(j=0;j<cpoints;j++)
         {

             if(up)
             {
             //_____add polygons to object
             p = new MyPolygon();
            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(apex);
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);
              }
              else
              {
                 p = new MyPolygon();
                p.addPoint(center.add(grid[t][j]));
               p.addPoint(apex);
              p.addPoint(center.add(grid[t][(j+1)%cpoints]));


            this.add_polygon(p,color);
            }



          }

         
        objcenter=center.vclone();
    }   // end circle



    public void create_cylinder(Vector center,double radius,double height,Vector color)
    {


    int cpoints=wireframe;

    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;

      double h=0;
      double r=radius;



      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;
        double x=r*Math.cos(cangle);
        double z=-r*Math.sin(cangle);

        //___add this point to grid
        grid[turn][j]=new Vector(x,h,z);


      } // for j


      //_________add apex polygons
           Vector apex=new Vector(0,height,0);

            int t=turn;

         // ____ main body
         for(j=0;j<cpoints;j++)
         {


             //_____add polygons to object
            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
             p.addPoint(center.add(grid[t][(j+1)%cpoints]).add(apex));
             p.addPoint(center.add(grid[t][(j)%cpoints]).add(apex));


            this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[t][(j)%cpoints]).add(apex));
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);

          }


           create_circle(center,radius,color,false);
            create_circle(center.add(apex),radius,color,true);

      Vector apex1=new Vector(0,height/2,0);
        objcenter=center.add(apex1).vclone();


    }   // end cylinder



	int recDepth =8;

	tree tre;

	class tree
	{
		double lAngle=40;
		double rAngle=35;
	    double mBr=0.7;
		double lBr=0.4;
		double rBr=0.4;
		MyPoint bottom = new MyPoint(0.01,0);
		MyPoint top = new MyPoint(0,40);
	}

  class MyPoint
  {
    public double x,y;

    MyPoint(double x1,double y2)
    {
      x=x1;
      y=y2;
      }

      MyPoint()
      {
      }
  }

  private Vector rcolor(Vector c)
  {

    double change=m.nextDouble()/2+.5;

    double tc=c.get_y()*change;
    if(tc>1.0)
      tc=1;
    Vector r=new Vector(c.get_x(),tc,c.get_z());
      return r;
    }

	public void drawTree(tree t,int r,double angle)
	{
		if (r<=0) return;


		MyPoint lp = new MyPoint();
		MyPoint rp = new MyPoint();
		MyPoint mp = new MyPoint();

		double len = Math.pow(Math.pow(t.bottom.x-t.top.x,2)+Math.pow(t.bottom.y-t.top.y,2),0.5);
		double mAngle=0;
		double dx = t.top.x-t.bottom.x;
		double dy = t.top.y-t.bottom.y;



		double temp=0;
		if(dx==0)
		{
			dx++;

		}
		temp = Math.atan(Math.abs(dy/dx));
	
		if((dx<0)&&(dy<0)) 		{	mAngle=Math.PI+temp;	}
		if((dx<0)&&(dy>0)) 		{	mAngle=Math.PI-temp;	}
		if((dx>0)&&(dy<0)) 		{	mAngle=-temp;			}
		if((dx>=0)&&(dy>=0)) 	{	mAngle=temp;			}

		mp.x=t.bottom.x*t.mBr + t.top.x*(1-t.mBr);
		mp.y=t.bottom.y*t.mBr + t.top.y*(1-t.mBr);
	    double lBrLen  = t.lBr*len;
		double rBrLen  = t.rBr*len;
		lp.x = (mp.x	+ lBrLen*Math.cos(t.lAngle*Math.PI/180 + mAngle));
		lp.y = (mp.y	+ lBrLen*Math.sin(t.lAngle*Math.PI/180+ mAngle));
		rp.x = (mp.x	+ rBrLen*Math.cos(-(t.rAngle*Math.PI/180) + mAngle));
		rp.y = (mp.y	+ rBrLen*Math.sin(-(t.rAngle*Math.PI/180)+ mAngle));

	 //	g2d.drawLine(t.bottom,t.top);
	//	g2d.drawLine(mp,lp);
	//	g2d.drawLine(mp,rp);


  Matrix rotm=new Matrix();
    rotm.rotate_y(angle);

    MyPolygon p = new MyPolygon();

            p.addPoint(rotm.vec_postmultiply(new Vector(t.bottom.x,t.bottom.y,0D)));
             p.addPoint(rotm.vec_postmultiply(new Vector(t.top.x,t.top.y,0)));
            // p.addPoint(new Vector(t.bottom.x,t.bottom.y,0));


            this.add_polygon(p,current_colour);

          p = new MyPolygon();
            //double sgn = Math.random();
            //double z_off = 1.5*lBrLen*Math.random()*(sgn-0.5)/Math.abs(sgn-0.5);
            p.addPoint(rotm.vec_postmultiply(new Vector(mp.x,mp.y,0)));
            p.addPoint(rotm.vec_postmultiply(new Vector(lp.x,lp.y,0)));
           // p.addPoint(new Vector(mp.x,mp.y,0));




            this.add_polygon(p,current_colour);

         p = new MyPolygon();
             //z_off = -1.5*rBrLen*Math.random()*(sgn-0.5)/Math.abs(sgn-0.5);
            p.addPoint(rotm.vec_postmultiply(new Vector(mp.x,mp.y,0)));
             p.addPoint(rotm.vec_postmultiply(new Vector(rp.x,rp.y,0)));
            // p.addPoint(new Vector(mp.x,mp.y,0));


            this.add_polygon(p,current_colour);




		r--;
		tree tr=new tree();
		tr.lAngle=t.lAngle;
		tr.rAngle=t.rAngle;

		tr.lBr=t.lBr;
		tr.rBr=t.rBr;
		tr.mBr=t.mBr;
		tr.top = lp;
		tr.bottom = mp;

    current_colour=rcolor(orig_colour);

		drawTree(tr,r,angle);

		tr.top = rp;
		tr.bottom = mp;

    current_colour=rcolor(orig_colour);
		drawTree(tr,r,angle);

		tr.top = t.top;
		tr.bottom = mp;

    current_colour=rcolor(orig_colour);
		drawTree(tr,r,angle);

	}


	public void draw(double langle,double rangle, double lbr,double rbr,double mbr,double top,int recd,int steps)
	{
       tre=new tree();
       tre.lAngle=langle;
       tre.rAngle=rangle;
       tre.lBr=lbr;
       tre.rBr=rbr;
       tre.mBr=mbr;
       tre.top=new MyPoint(0,top);
       recDepth=recd;

        orig_colour=current_colour;


       for(int i=0;i<steps;i++)
       {
          double angle = 2*Math.PI*i/steps;

			  drawTree(tre,recDepth,angle);
        }
	}

    public void create_line(Vector l1,Vector l2,Vector color)
    {


    double radius=0.2;
    double height=(l2.subtract(l1)).length();
    Vector center=l1;
    int cpoints=wireframe;

    System.out.println("creating line");
    Vector grid[][]=new Vector[2][cpoints];

//    grid[0]=new Vector[16];
 //   grid[1]=new Vector[16];


    int i,j,k;
    int turn=0;

      double h=0;
      double r=radius;



      for(j=0;j<cpoints;j++)
      {
        double cangle=(Math.PI*2)*j/cpoints;
        double x=r*Math.cos(cangle);
        double z=-r*Math.sin(cangle);

        //___add this point to grid
        grid[turn][j]=new Vector(x,h,z);


      } // for j


      //_________add apex polygons
           Vector apex=new Vector(0,height,0);

            int t=turn;

         // ____ main body
         for(j=0;j<cpoints;j++)
         {


             //_____add polygons to object
            MyPolygon p = new MyPolygon();

            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
             p.addPoint(center.add(grid[t][(j+1)%cpoints]).add(apex));
             p.addPoint(center.add(grid[t][(j)%cpoints]).add(apex));


            this.add_polygon(p,color);

           p = new MyPolygon();

            p.addPoint(center.add(grid[t][(j+1)%cpoints]));
            p.addPoint(center.add(grid[t][(j)%cpoints]).add(apex));
            p.addPoint(center.add(grid[t][j]));

            this.add_polygon(p,color);

          }


           create_circle(center,radius,color,false);
            create_circle(center.add(apex),radius,color,true);

      Vector apex1=new Vector(0,height/2,0);
        objcenter=center.add(apex1).vclone();



      Vector dir=l2.subtract(l1);
        dir.normalize();
        double d=dir.get_y()*dir.get_y()+dir.get_z()*dir.get_z();

        d=Math.sqrt(d);

         double d1=dir.get_x()*dir.get_x()+dir.get_y()*dir.get_y();

        d1=Math.sqrt(d1);

        double ysign=1;

          if(Math.abs(dir.get_y())>0)
            ysign =dir.get_y()/Math.abs(dir.get_y());

        double xsign=1;
         if(Math.abs(dir.get_x())>0)
            xsign =dir.get_x()/Math.abs(dir.get_x());

        double zsign=1;
           if(Math.abs(dir.get_z())>0)
            zsign =dir.get_z()/Math.abs(dir.get_z());


          rotate_x(l1,Math.acos(d1));

             if(d>=0)
          rotate_z(l1,-Math.acos(Math.abs(dir.get_y())/d1));



      



    }   // end line


}  // class GraphicsObject

class ImgObs implements ImageObserver
{
  int height;
  int width;
  boolean avail=false;
  public boolean imageUpdate(Image img,int inf ,int x,int y,int w,int h)
  {
    if(inf>=ImageObserver.HEIGHT +ImageObserver.WIDTH)
    {
       height = h;
       width = w;
       avail = true;
       return false;
    }
    return true;
  }
}
