Back to index

wims  3.65+svn20090927
QuadricObject.java
Go to the documentation of this file.
00001 package rene.zirkel.objects;
00002 
00003 // file: QuadricObject.java
00004 
00005 import java.util.Enumeration;
00006 
00007 import rene.util.xml.XmlWriter;
00008 import rene.zirkel.Zirkel;
00009 import rene.zirkel.ZirkelCanvas;
00010 import rene.zirkel.construction.Construction;
00011 import rene.zirkel.construction.Count;
00012 import rene.zirkel.dialogs.EditConditionals;
00013 import rene.zirkel.dialogs.ObjectEditDialog;
00014 import rene.zirkel.expression.Quartic;
00015 import rene.zirkel.graphics.MyGraphics;
00016 import rene.zirkel.graphics.PolygonDrawer;
00017 
00018 public class QuadricObject extends ConstructionObject
00019        implements PointonObject, MoveableObject
00020 {      PointObject P[];
00021        static Count N=new Count();
00022        double X[];
00023        
00024        public QuadricObject (Construction c, PointObject p[])
00025        {      super(c);
00026               P=p;
00027               validate();
00028               updateText();
00029        }
00030 
00031        public String getTag () { return "Quadric"; }
00032        public int getN () { return N.next(); }
00033        
00034        public void updateText ()
00035        {      try
00036               {      String Names[]=new String[P.length];
00037                      for (int i=0; i<P.length; i++) Names[i]=P[i].getName();
00038                      setText(textAny(Zirkel.name("text.quadric"),Names));
00039               } 
00040               catch (Exception e) {}
00041        }
00042        
00043        public void validate ()
00044        {      for (int i=0; i<P.length; i++)
00045                      if (!P[i].valid())
00046                      {      Valid=false; return;
00047                      }
00048               Valid=true;
00049               
00050               // Baue Koeffizientenmatrix auf (x^2,y^2,x,y,xy,1):
00051               double A[][]=new double[5][6];
00052               for (int i=0; i<5; i++)
00053               {      double x=P[i].getX(),y=P[i].getY();
00054                      A[i][0]=x*x; A[i][1]=y*y;
00055                      A[i][2]=x; A[i][3]=y;
00056                      A[i][4]=x*y; A[i][5]=1;
00057                      double sum=0;
00058                      for (int j=0; j<6; j++) sum+=A[i][j]*A[i][j];
00059                      sum=Math.sqrt(sum);
00060                      for (int j=0; j<6; j++) A[i][j]/=sum;
00061               }
00062               
00063               // Gaußverfahren, um auf untere Dreiecksmatrix zu kommen
00064               int r=0;
00065               int colindex[]=new int[6]; // Index der Stufe oder -1 (keine Stufe)
00066               // Iteration über alle Spalten:
00067               for (int c=0; c<6; c++)
00068               {      if (r>=5) // Schema schon fertig
00069                      {      colindex[c]=-1; continue;
00070                      }
00071                      // Berechne Pivotelement mit spaltenweiser Maximumssuche
00072                      double max=Math.abs(A[r][c]);
00073                      int imax=r;
00074                      for (int i=r+1; i<5; i++)
00075                      {      double h=Math.abs(A[i][c]);
00076                             if (h>max)
00077                             {      max=h; imax=i;
00078                             }
00079                      }
00080                      if (max>1e-13)
00081                      {      // Vertausche Zeilen:
00082                             if (imax!=r)
00083                             {      double h[]=A[imax];
00084                                    A[imax]=A[r];
00085                                    A[r]=h;
00086                             }
00087                             // Mache restliche Spalte zu 0:
00088                             for (int i=r+1; i<5; i++)
00089                             {      double lambda=A[i][c]/A[r][c];
00090                                    for (int j=c+1; j<6; j++)
00091                                           A[i][j]-=lambda*A[r][j];
00092                             }
00093                             colindex[c]=r;
00094                             r++;
00095                      }
00096                      else
00097                      {      colindex[c]=-1;
00098                      }
00099               }
00100               // Berechne die x-Werte:
00101               X=new double[6];
00102               for (int j=5; j>=0; j--)
00103               {      if (colindex[j]<0)
00104                      {      X[j]=1;
00105                      }
00106                      else
00107                      {      double h=0;
00108                             int i=colindex[j];
00109                             for (int k=j+1; k<6; k++)
00110                                    h+=A[i][k]*X[k];
00111                             X[j]=-h/A[i][j];
00112                      }
00113               }
00114               // Normalisiere
00115               double sum=0;
00116               for (int i=0; i<=5; i++) sum+=Math.abs(X[i]);
00117               if (sum<1e-10) Valid=false;
00118               for (int i=0; i<=5; i++) X[i]/=sum;
00119        }
00120        
00121        public void paint (MyGraphics g, ZirkelCanvas zc)
00122        {      if (!Valid || mustHide(zc)) return;
00123               g.setColor(this);
00124               // Draw the lower part of the quadrik (minus the root):
00125               double start=zc.minX(),x=start;
00126               double end=zc.maxX();
00127               double h=zc.dx(1);
00128               boolean valid=false,ptext=false;
00129               int c0=0,r0=0,ctext=20,rtext=20;
00130               PolygonDrawer pd=new PolygonDrawer(g,this);
00131               // Draw the lower part of the quadric (plus the root):
00132               while (x<=end)
00133               {      try
00134                      {      double y=computeLower(x);
00135                             int c=(int)zc.col(x),r=(int)zc.row(y);
00136                             if (valid)
00137                             {      pd.drawTo(c,r);
00138                                    if (!ptext && r0-r>c-c0 && zc.isInside(x,y))
00139                                    {      ctext=c; rtext=r; ptext=true; 
00140                                    }
00141                             }
00142                             else pd.startPolygon(c,r);
00143                             c0=c; r0=r;
00144                             valid=true;
00145                      }
00146                      catch (RuntimeException e)
00147                      {      valid=false;
00148                      }
00149                      x+=h;
00150               }
00151               pd.finishPolygon();
00152               // Draw the upper part of the quadric (plus the root):
00153               x=start-2*h;
00154               valid=false;
00155               while (x<=end+2*h)
00156               {      try
00157                      {      double y=computeUpper(x);
00158                             int c=(int)zc.col(x),r=(int)zc.row(y);
00159                             if (valid)
00160                             {      pd.drawTo(c,r);
00161                                    // Try to find a position for the label:
00162                                    if (!ptext && r0-r>c-c0 && zc.isInside(x,y))
00163                                    {      ctext=c; rtext=r; ptext=true; 
00164                                    }
00165                             }
00166                             else // left edge of quadric, connect with lower part
00167                             {      try
00168                                    {      double y1=computeLower(x);
00169                                           if (x>=start-h && x<=end+h)
00170                                                  g.drawLine(c,zc.row(y1),c,r,this);
00171                                    }
00172                                    catch (RuntimeException e) {}
00173                                    pd.startPolygon(c,r);
00174                             }
00175                             c0=c; r0=r;
00176                             valid=true;
00177                      }
00178                      catch (RuntimeException e) // no points in that range
00179                      {      if (valid) // we just left the right edge of the quadric
00180                             {      try
00181                                    {      double y1=computeLower(x-h);
00182                                           if (x-h>=start-h && x-h<=end+h)
00183                                                  g.drawLine(c0,zc.row(y1),c0,r0,this);
00184                                    }
00185                                    catch (RuntimeException ex) {}
00186                             }
00187                             valid=false;
00188                      }
00189                      x+=h;
00190               }
00191               pd.finishPolygon();
00192               String s=getDisplayText();
00193               if (!s.equals(""))
00194               {      g.setLabelColor(this);
00195                      setFont(g);
00196                      DisplaysText=true;
00197                      TX1=ctext+zc.col(XcOffset)-zc.col(0);
00198                      TY1=rtext+zc.row(YcOffset)-zc.row(0);
00199                      drawLabel(g,s);
00200               }
00201        }
00202        
00203        static public String Tags[]={"x^2","y^2","x","y","xy"};
00204        
00205        public String getDisplayValue ()
00206        {      String s="";
00207               for (int i=0; i<5; i++)
00208                      s=s+helpDisplayValue(i==0,-X[i],Tags[i]);
00209               return s+"="+roundDisplay(X[5]);
00210        }
00211        
00212        public String getEquation ()
00213        {      return getDisplayValue();
00214        }
00215        
00216        public boolean nearto (int cc, int rr, ZirkelCanvas zc)
00217        {      if (!displays(zc)) return false;
00218               int size=(int)zc.selectionSize();
00219               double start=zc.minX(),x=start;
00220               double end=zc.maxX();
00221               double h=zc.dx(1);
00222               while (x<=end)
00223               {      try
00224                      {      double y=computeUpper(x);
00225                             double c=zc.col(x),r=zc.row(y);
00226                             if (Math.abs(cc-c)<=size*3/2 && Math.abs(rr-r)<=size*3/2)
00227                                    return true;
00228                      }
00229                      catch (Exception e) {}
00230                      try
00231                      {      double y=computeLower(x);
00232                             double c=zc.col(x),r=zc.row(y);
00233                             if (Math.abs(cc-c)<=size*3/2 && Math.abs(rr-r)<=size*3/2)
00234                                    return true;
00235                      }
00236                      catch (Exception e) {}
00237                      x+=h;
00238               }
00239               return false;
00240        }
00241               
00242        public void edit (ZirkelCanvas zc)
00243        {      ObjectEditDialog d=new ObjectEditDialog(zc.getFrame(),"",this);
00244               d.setVisible(true);
00245               zc.repaint();
00246               if (d.wantsMore())
00247               {      new EditConditionals(zc.getFrame(),this);
00248                      validate();
00249               }
00250        }
00251 
00252        double computeUpper (double x)
00253        {      if (Math.abs(X[1])>1e-13)
00254               {      double p=(X[3]+x*X[4])/X[1],q=(X[0]*x*x+X[2]*x+X[5])/X[1];
00255                      double h=p*p/4-q;
00256                      if (h<0) throw new RuntimeException("");
00257                      return -p/2+Math.sqrt(h);
00258               }
00259               else
00260               {      return -(X[0]*x*x+X[2]*x+X[5])/(X[3]+X[4]*x);
00261               }
00262        }
00263        
00264        double computeLower (double x)
00265        {      if (Math.abs(X[1])>1e-13)
00266               {      double p=(X[3]+x*X[4])/X[1],q=(X[0]*x*x+X[2]*x+X[5])/X[1];
00267                      double h=p*p/4-q;
00268                      if (h<0) throw new RuntimeException("");
00269                      return -p/2-Math.sqrt(h);
00270               }
00271               else throw new RuntimeException("");
00272        }
00273        
00274        public void printArgs (XmlWriter xml)
00275        {      for (int i=0; i<P.length; i++)
00276                      xml.printArg("point"+(i+1),P[i].getName());
00277        }
00278 
00279        public Enumeration depending ()
00280        {      DL.reset();
00281               for (int i=0; i<P.length; i++) DL.add(P[i]);
00282               return DL.elements();
00283        }
00284 
00285        public void translate ()
00286        {      for (int i=0; i<P.length; i++)
00287                      P[i]=(PointObject)P[i].getTranslation();
00288        }
00289 
00290        public ConstructionObject copy ()
00291        {      try
00292               {      QuadricObject o=(QuadricObject)clone();
00293                      setTranslation(o);
00294                      o.P=new PointObject[P.length];
00295                      for (int i=0; i<P.length; i++) o.P[i]=P[i];
00296                      o.translateConditionals();
00297                      o.translate();
00298                      o.setName();
00299                      o.updateText();
00300                      o.setBreak(false);
00301                      o.setTarget(false);
00302                      return o;
00303               }
00304               catch (Exception e)
00305               {      return null; 
00306               }
00307        }
00308 
00309        public boolean onlynearto (int x, int y, ZirkelCanvas zc)
00310        {      return false;
00311        }
00312 
00313        public boolean equals (ConstructionObject o)
00314        {      if (!(o instanceof QuadricObject) || !o.valid()) return false;
00315               try
00316               {      for (int i=0; i<6; i++)
00317                      {      if (!equals(X[i],((QuadricObject)o).X[i]))
00318                                    return false;
00319                      }
00320               }
00321               catch (RuntimeException e)
00322               {      return false;
00323               }
00324               return true;
00325        }
00326        
00327        public boolean hasUnit ()
00328        {      return false;
00329        }
00330 
00331        public void project (PointObject P) 
00332        {      double a=X[0],b=X[1],c=X[2],d=X[3],e=X[4],r=X[5];
00333               double xc=P.getX(),yc=P.getY();
00334               if (Math.abs(a*xc*xc+b*yc*yc+c*xc+d*yc+e*xc*yc+r)<1e-13) // close enough
00335                      return;
00336               double t[]=new double[5],s[]=new double[5],si[]=new double[5];
00337               // Coefficients for fourth order polynomial for lambda (Lagrange factor)
00338               // Minimize (x-xc)^2+(y-yc)^2 with a*x^2+b*y^2+c*x+d*y+e*x*y+r=0
00339               // Computed with Maple
00340               t[0] = a*e*e*d*d-4*a*b*b*c*c+4*a*e*d*b*c-4*b*a*a*d*d+b*c*c*e*e
00341                      -c*Math.pow(e,3)*d+r*Math.pow(e,4)-8*r*e*e*b*a+16*r*b*b*a*a;
00342               t[1] = 8*b*b*c*c+8*a*a*d*d-8*e*d*b*c-8*a*d*c*e+8*r*e*e*b+8*a*b*c*c
00343                      +8*b*a*d*d+8*r*e*e*a-32*r*b*b*a-32*r*b*a*a;
00344               t[2] = 12*e*d*c+16*r*b*b-4*b*d*d-8*r*e*e+4*e*e*d*yc+16*b*b*xc*c-16*b*c*c
00345                      -16*a*d*d-4*a*c*c+16*r*a*a+16*a*a*d*yc+4*xc*e*e*c-8*e*d*b*xc-8*e*yc*b*c
00346                      -8*a*d*xc*e-8*a*yc*c*e+16*a*b*b*xc*xc-4*a*e*e*yc*yc+16*b*a*a*yc*yc
00347                      -4*b*xc*xc*e*e+4*Math.pow(e,3)*yc*xc+64*r*b*a-16*a*b*xc*e*yc;
00348               t[3] = -32*r*b+8*d*d+8*c*c+16*e*d*xc+8*e*e*yc*yc+8*xc*xc*e*e-32*r*a
00349                      -32*b*xc*c+16*e*yc*c-32*a*d*yc-32*a*b*xc*xc-32*b*a*yc*yc;
00350               t[4] = 16*b*yc*yc+16*d*yc+16*c*xc+16*xc*e*yc+16*r+16*a*xc*xc;
00351               int k=Quartic.solve(t,s);
00352               // System.out.println(k+"Solutions found.");
00353               double dmin=1e30,xmin=xc,ymin=yc;
00354               for (int i=0; i<k; i++) // Choose closest solution of Lagrange equation
00355               {      double l=s[i];
00356                      // Solve for x,y when lambda is known.
00357                      // Computed with Maple
00358                      double px = -(-e*d+4*b*l*xc-2*e*l*yc-4*l*l*xc+2*b*c-2*l*c)/(-e*e+4*b*a-4*b*l-4*l*a+4*l*l);
00359                      double py = -(2*a*d+4*a*l*yc-2*l*d-4*l*l*yc-2*l*xc*e-c*e)/(-e*e+4*b*a-4*b*l-4*l*a+4*l*l);
00360                      double dist=(px-xc)*(px-xc)+(py-yc)*(py-yc);
00361                      if (dist<dmin)
00362                      {      dmin=dist; xmin=px; ymin=py;
00363                      }
00364               }
00365               P.move(xmin,ymin);
00366        }
00367 
00368        public void project (PointObject P, double alpha) 
00369        {      project(P);
00370        }
00371 
00372        public void dragTo (double x, double y) 
00373        {      for (int i=0; i<5; i++)
00374               {      P[i].move(xd[i]+(x-x1),yd[i]+(y-y1));
00375               }
00376        }
00377 
00378        public void move (double x, double y) 
00379        {
00380        }
00381 
00382        public boolean moveable () 
00383        {      for (int i=0; i<5; i++)
00384               {      if (!P[i].moveable())
00385                             return false;
00386               }
00387               return true;
00388        }
00389        
00390        double xd[],yd[],x1,y1;
00391 
00392        public void startDrag (double x, double y) 
00393        {      if (xd==null)
00394               {      xd=new double[5];
00395                      yd=new double[5];
00396               }
00397               for (int i=0; i<5; i++)
00398               {      xd[i]=P[i].getX(); yd[i]=P[i].getY();
00399               }
00400               x1=x; y1=y;
00401        }
00402 
00403        public void snap (ZirkelCanvas zc)
00404        {      if (moveable())
00405               {      for (int i=0; i<5; i++)
00406                      {      P[i].snap(zc);
00407                      }
00408               }
00409        }
00410 
00411        public boolean canInteresectWith(ConstructionObject o) 
00412        {      return true;
00413        }
00414 
00415 
00416 }