import java.util.*;
import java.awt.*;
import java.applet.Applet;

class shape extends Object { // quadrilatere x[0],y[0]  x[1],y[1] ...x[3],y[3]
    protected shape prev=null;
    drawing dr;
    int x[]= new int[5];// abscisses des sommets
    int y[]= new int[5];// ordonnées des sommets
    double index = 1.49;
    double a0; double a1; double a2; // fonctions des sommets pour le calcul du déterminant
    double sqrtVa=1; double sqrtVb=1;  double indexNorm2=0;
    static int nGauss = 7; // coeff Gauss Legendre Dhatt-Touzot p 285
    static double z[]={ 0,  .405845151377397, .741531185599394, .949107912342759,- .405845151377397,-.741531185599394,-.949107912342759};
    static double w[]={.417959183673469, .381830050505119, .279705391489277, .129484966168870,.381830050505119,.279705391489277, .129484966168870};
    static final double sqPi = Math.sqrt(Math.PI);
	  private Polygon pol = null; 
    public int s=0; // number of nodes (normally : 4 when built)
    public Color kolor = Color.blue;
    public  int id;
    public shape() {}
    public shape(shape prev_, drawing dr_) {prev = prev_; dr = dr_;  id = (prev==null)?1: prev.id+1;}
	  public int toInst(int k, double xx, double yy)
							{ if (k==0) { index = xx/dr.scaleX;} 
    						else {x[k-1]=(int)Math.round(xx); y[k-1]= (int)Math.round(yy); s= k++;}
	  					return(k-1);}
	  public static boolean valid(shape sh) {return((sh instanceof shape) && (sh.s==4)) ;}
    public Polygon pol_() { return(pol);}
    public void  setPrev(shape prev_) {this.prev = prev_;}
    public void setRect( double xmin, double xmax, double ymin, double ymax, double index )
        { this.x[0] = this.x[3] = (int)Math.round(xmin);  this.x[2] =  this.x[1] = (int) Math.round(xmax);
          this.y[0] = this.y[1] = (int) Math.round( ymin);  this.y[2] =  this.y[3] = (int) Math.round(ymax);
          this.index = index; this.s = 4;
        }
   public Rectangle rect(){ int xmin = x[0]; int ymin = y[0];int xmax = x[0]; int ymax = y[0] ;
          for (int i=1; i<4; i++){ if (x[i]>xmax) xmax=x[i];
                                   if (y[i]>ymax) ymax=y[i];
                                   if (x[i]<xmin) xmin=x[i];
                                   if (y[i]<ymin) ymin=y[i];}
                              return(new Rectangle(xmin,ymin,xmax - xmin, ymax-ymin));
												}
   public Point basePoint() { return( new Point(x[0],y[0]));}
   public void _dr(drawing d) { this.dr = d;this.pol=null;}
   public shape prev_() { return (this.prev);}
   public double integral(int mu1, int mu2, int nu1, int nu2)
        // Somme sur l'élément réel de f(x,y)dx dy =
        //     somme sur (-1,+1) dans les deux dimensions de f(x(etha,dzeta),y(etha,dzeta))*det(J(etha,dzeta)*d_etha*d_dzeta
        { double S=0; double sp; double xx,yy;
         for (int i=0; i<nGauss; i++)
            {sp=0; for (int j=0;j<nGauss; j++)
                { xx = of_dzeta_etha(z[i], z[j],(double)x[0], (double)x[1],(double)x[2],(double) x[3]);
                  yy = of_dzeta_etha(z[i], z[j],(double)y[0], (double)y[1], (double)y[2], (double)y[3]);
                  xx *= sqrtVa; yy *= sqrtVb;
                  sp += w[j]*h_integrand(xx,yy, mu1, mu2, nu1, nu2)*detJ(z[i],z[j]); }
             S += (w[i]*sp);
              }
        if (mu1 >= 0) S *= indexNorm2/(dr.halfX*dr.halfY);
        return(S);
        }
    public double of_dzeta_etha(double dzeta, double etha, double u1, double u2, double u3, double u4)
         // fonction d'interpolation pour un élément bilinéaire
        {u1 *= (1-dzeta)*(1-etha);
        u2 *= (1+dzeta)*(1-etha);
        u3 *= (1+dzeta)*(1+etha);
        u4 *= (1-dzeta)*(1+etha);
        return((u1 +u2 +u3 +u4)/4);
        }
    public double detJ(double dzeta, double etha)    //det(Jacobienne) utile pour calculer l'integrale sur un element simple en etha, dzeta  
        {    if  ((a0==0) && (a1==0) && (a2==0))
                  {a0= ((y[3]-y[1])*(x[2]-x[0]) -(y[2]-y[0])*(x[3]-x[1]))/((double)8);
                   a1= ((y[2]-y[3])*(x[1]-x[0]) -(y[1]-y[0])*(x[2]-x[3]))/((double)8);
                   a2= ((y[3]-y[0])*(x[2]-x[1]) -(y[2]-y[1])*(x[3]-x[0]))/((double)8);}
            return( a0 + a1*dzeta + a2*etha);
        }
    public void prepare(double va, double vb, double delta2)
        { this.sqrtVa= Math.sqrt(va)/dr.halfX; this.sqrtVb =Math.sqrt(vb)/dr.halfY;
          indexNorm2= this.index/dr.index;
          indexNorm2 *=indexNorm2; indexNorm2 -=1; indexNorm2 /= (delta2 -1); }
    public double h_integrand(double xx, double yy, int mu1, int mu2, int nu1, int nu2)
        {  double phiMu1 = gaussHermite(mu1, xx);
           double phiMu2 = gaussHermite(mu2, xx);
           double phiNu1 = gaussHermite(nu1,yy);
           double phiNu2 = gaussHermite(nu2, yy);
           return(phiMu1*phiMu2*phiNu1*phiNu2);
        }
   public double gaussHermite(int mu, double X)
        {double Hx =1; double Hxp=0; int fact_2mu=1;
           if (mu<0) return(1);
           for (int j=0; j<mu; j++)
           { double Hxs = Hx; Hx  = (X + X)*Hx - 2*j*Hxp;Hxp=Hxs;
             fact_2mu *= ( j+1)*2; }
           return( Hx*Math.exp(-X*X/2)/Math.sqrt(sqPi*fact_2mu));
        }
           
   public double positiveSurface()
      { double q=0; if ((q=this.s_integral(-1,-1,-1,-1))<=0)
         { dr.appl.displayMsg(" permuting nodes => area>0,");
           int ss=x[1];x[1]=x[3];x[3]=ss;ss=y[1];y[1]=y[3];y[3]=ss;a0=a1=a2=0;
           this.toProp(1,x[1],y[1],dr.appl);
           this.toProp(3,x[3],y[3],dr.appl);
           q=this.s_integral(-1,-1,-1,-1);}// node 1 and 3 exchanged if S<0
         return(q/(dr.scaleX*dr.scaleY));}
   public void draw(int xx, int yy)
        {dr.gr.setColor(this.kolor);
           x[s]=xx; y[s]=yy;
          switch (s) { case(0): dr.gr.drawRect(xx,yy,3,3);break;
                       case(1): case(2): for (int i=0; i< s;i++) { dr.gr.drawLine(x[i],y[i],x[i+1],y[i+1]); }break;
                       case(3): 
                       case(4): x[4]=x[0]; y[4]=y[0];
                       pol=new Polygon(x,y,5); dr.gr.drawPolygon(pol);
                      }
        }
  public void redraw()
        { if (pol instanceof Polygon) { dr.gr.setColor(this.kolor); dr.gr.drawPolygon(pol);}
           else draw(x[s],y[s]);
         }
	public void mark(){ }
  public void rescale(double multX,  double multY)
         { pol = null; a0=a1=a2=0;// polygon picture and determinant to rebuild
            for (int i=0; i<4; i++) { x[i] -= dr.centerX; y[i] -= dr.centerY;
                                      x[i] *= multX; y[i] *= multY;
                                      x[i] += dr.centerX; y[i] += dr.centerY;}
        }
   public int toProp(int i, double xx, double yy,modAppl appl) // si i <0, xx and yy are scaleX, and scaleY, all summits are displayed
        {     if (i<0)    {for (i=0; i<s; i++) this.toProp(i, x[i],y[i],appl); return(s);}
              if (!(appl.indexField instanceof TextField)) return(0);
               appl.indexField.setText(String.valueOf(index));
               appl.wl_nbLabel.setText("shape # :");
               appl.wl_nbField.setText((new Character((char)(64+id))).toString());
               if (appl.xField.isEmpty()) return(0);
             xx -= dr.centerX; xx/=dr.scaleX;
        	   yy -= dr.centerY; yy/=dr.scaleY;
             ((TextField)appl.xField.elementAt(i)).setText(String.valueOf(xx));
             ((TextField)appl.yField.elementAt(i)).setText(String.valueOf(-yy));
           return(1);
        } 
     public int fromProp(int i, double xx, double yy, modAppl appl) // si i <0, xx and yy are scaleX, and scaleY, all summits are displayed
        {     int k;
              if (i<0) 
              {for (i=0; i<s; i++)  this.fromProp(i, xx,yy,appl);
                index= Double.valueOf( appl.indexField.getText()).doubleValue();
                String str = appl.wl_nbField.getText();// is there a special code ?
              	if (str.indexOf('r')>=0) { // transformed to rect
                        rect rr = new rect(null,this.dr); rr.id = this.id;
                        rr.setRect((double)this.x[0],(double)this.x[2],(double)this.y[0],(double)this.y[2],this.index);
                        Rectangle rc=this.rect();// to reorder nodes
                        rr.setRect((double)rc.x,(double)(rc.width+rc.x),(double)rc.y,(double)(rc.height+rc.y),this.index);
                        appl.displayMsg("\nshape "+this.id+" substituted to rectangle");
                        return(appl.substitute(rr,this));}
                if (str.indexOf('s')>=0) { // transformed to shape
                        shape rr = new shape(null,this.dr); rr.id = this.id;
                        rr.setRect((double)this.x[0],(double)this.x[2],(double)this.y[0],(double)this.y[2],this.index);
                        appl.displayMsg("\nshape "+this.id+" substituted to shape");
                        return(appl.substitute(rr,this));}
                if ((k=str.indexOf('m'))>=0) { int k1 = str.indexOf(',');// move (offset) shape
                        int dX = (int) Math.round(Double.valueOf(str.substring(k+1,k1)).doubleValue()*appl.pane.scaleX);
                        int dY = (int)  Math.round(Double.valueOf(str.substring(k1+1)).doubleValue()*appl.pane.scaleY);
                        appl.displayMsg("\nshape "+this.id+" moving "+dX+", "+dY);
                        this.offset(dX,dY);
                        this.toProp(-1,appl.pane.scaleX,appl.pane.scaleY,appl);}
                if ((k=str.indexOf('t'))>=0) { // turn it
                        if (this instanceof rect) {appl.displayMsg("\nfirst, substitute it to shape!");return(10);}
                        this.offset(-dr.centerX,-dr.centerY);
                        this.rotate(Double.valueOf(str.substring(k+1)).doubleValue());
                        this.offset(dr.centerX,dr.centerY);
                        this.toProp(-1,dr.scaleX,dr.scaleY,appl);
                        }
                if (str.indexOf('a')>=0) { // compute area
                       double q= this.positiveSurface();
                       appl.displayMsg("\narea of shape "+this.id+" : "+q+" um²");}
              	return(this.id);}
 // reading one node       	
              if (appl.xField.isEmpty()) return(0);
              x[i]= (int)Math.round(xx*Double.valueOf( ((TextField)appl.xField.elementAt(i)).getText()).doubleValue());
              y[i]= (int)Math.round(-yy*Double.valueOf( ((TextField)appl.yField.elementAt(i)).getText()).doubleValue());
           x[i] += dr.centerX; y[i] += dr.centerY;//modif : shapes prop are centered   
           pol = null; this.a0=this.a1=this.a2=0;
           return(this.id);
        }
     public void rotate(double angle) { double ss = Math.sin(angle*Math.PI/180);
          double cs = Math.cos(angle*Math.PI/180);
          for (int i=0; i<=3; i++) {double xx= x[i]*cs-y[i]*ss; 
                                    y[i] = (int)Math.round(y[i]*cs+x[i]*ss);
                                    x[i]= (int)Math.round(xx);}
                                   }
    void  offset( int dx, int dy ) {  for (int i=0; i<=3; i++) { x[i] += dx; y[i] += dy;}
                                                }
    public double s_integral( double kx1,double kx2,double ky1, double ky2)
        // Somme sur l'élément réel de f(x,y)dx dy =
        //     somme sur (-1,+1) dans les deux dimensions de f(x(etha,dzeta),y(etha,dzeta))*det(J(etha,dzeta)*d_etha*d_dzeta
        { double S=0; double sp; double xx,yy;
         for (int i=0; i<nGauss; i++)
            {sp=0; for (int j=0;j<nGauss; j++)
                { xx = of_dzeta_etha(z[i], z[j], (double)x[0], (double)x[1],(double)x[2],(double) x[3]);
                  yy = of_dzeta_etha(z[i], z[j],(double) y[0], (double)y[1], (double)y[2], (double)y[3]);
                   sp += w[j]*s_integrand(xx,yy, kx1, kx2,ky1, ky2)*detJ(z[i],z[j]); }
             S += (w[i]*sp);
              }
        return(S);
        }
     private double s_integrand(double xx, double yy, double kx1,double kx2,double ky1, double ky2)
        { if (kx1<0) return (1.);//surface
          double ssx = (Math.cos((kx1-kx2)*xx) - Math.cos((kx1+kx2)*xx))/2.;
         // if (yy==0) appl.msg.appendText("\tx : "+xx+" ->f(x) : "+ssx);
          double ssy = Math.sin(ky1*yy)*Math.sin(ky2*yy);
          return(ssx*ssy);
        }
}// end class shape