/* Copyright 2006 Rene Grothmann, modified by Eric Hakenholz This file is part of C.a.R. software. C.a.R. is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. C.a.R. is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package rene.zirkel.objects; // file: QuadricObject.java import java.util.ArrayList; import java.util.Enumeration; import rene.util.xml.XmlWriter; import rene.gui.Global; import rene.zirkel.ZirkelCanvas; import rene.zirkel.construction.Construction; import rene.zirkel.construction.Count; import rene.zirkel.expression.ExpressionColor; import rene.zirkel.expression.Quartic; import rene.zirkel.graphics.MyGraphics; import rene.zirkel.graphics.PolygonDrawer; import rene.zirkel.structures.Complex; import rene.zirkel.structures.CoordinatesXY; public class QuadricObject extends ConstructionObject implements PointonObject, MoveableObject { public PointObject P[]; static Count N=new Count(); public double X[]; public QuadricObject(final Construction c, final PointObject p[]) { super(c); P=p; validate(); updateText(); } @Override public void setDefaults() { setShowName(Global.getParameter("options.quadric.shownames", false)); setShowValue(Global.getParameter("options.quadric.showvalues", false)); setColor(Global.getParameter("options.quadric.color", 0), Global.getParameter("options.circle.pcolor", (ExpressionColor) null, this)); setColorType(Global.getParameter("options.quadric.colortype", 0)); setHidden(Cn.Hidden); //setObtuse(Cn.Obtuse); //setSolid(Cn.Solid); setLarge(Cn.LargeFont); setBold(Cn.BoldFont); //setPartial(Cn.Partial); } @Override public void setTargetDefaults() { setShowName(Global.getParameter("options.quadric.shownames", false)); setShowValue(Global.getParameter("options.quadric.showvalues", false)); setColor(Global.getParameter("options.quadric.color", 0), Global.getParameter("options.circle.pcolor", (ExpressionColor) null, this)); setColorType(Global.getParameter("options.quadric.colortype", 0)); } @Override public String getTag() { return "Quadric"; } @Override public int getN() { return N.next(); } @Override public void updateText() { try { final String Names[]=new String[P.length]; for (int i=0; i intersect(final QuadricObject quad1, final QuadricObject quad2) { ArrayList points=new ArrayList(); // coeffs de x^2, xy, y^2, x, y et Cte : double a=quad1.X[0], b=quad1.X[4], c=quad1.X[1], d=quad1.X[2], e=quad1.X[3], f=quad1.X[5]; double aa=quad2.X[0], bb=quad2.X[4], cc=quad2.X[1], dd=quad2.X[2], ee=quad2.X[3], ff=quad2.X[5]; // System.out.println("*************"); // System.out.println("a="+a); // System.out.println("b="+b); // System.out.println("c="+c); // System.out.println("d="+d); // System.out.println("e="+e); // System.out.println("f="+f); // System.out.println("aa="+aa); // System.out.println("bb="+bb); // System.out.println("cc="+cc); // System.out.println("dd="+dd); // System.out.println("ee="+ee); // System.out.println("ff="+ff); double k1=-aa*b*bb*c+a*bb*bb*c+aa*aa*c*c+aa*b*b*cc-a*b*bb*cc-2*a*aa*c*cc+a*a*cc*cc; double k2=bb*bb*c*d-b*bb*cc*d-2*aa*c*cc*d+2*a*cc*cc*d-b*bb*c*dd+2*aa*c*c*dd+b*b*cc*dd-2*a*c*cc*dd-aa*bb*c*e+2*aa*b*cc*e-a*bb*cc*e-aa*b*c*ee+2*a*bb*c*ee-a*b*cc*ee; double k3=cc*cc*d*d-2*c*cc*d*dd+c*c*dd*dd-bb*cc*d*e-bb*c*dd*e+2*b*cc*dd*e+aa*cc*e*e+2*bb*c*d*ee-b*cc*d*ee-b*c*dd*ee-aa*c*e*ee-a*cc*e*ee+a*c*ee*ee+bb*bb*c*f-b*bb*cc*f-2*aa*c*cc*f+2*a*cc*cc*f-b*bb*c*ff+2*aa*c*c*ff+b*b*cc*ff-2*a*c*cc*ff; double k4=cc*dd*e*e-cc*d*e*ee-c*dd*e*ee+c*d*ee*ee+2*cc*cc*d*f-2*c*cc*dd*f-bb*cc*e*f+2*bb*c*ee*f-b*cc*ee*f-2*c*cc*d*ff+2*c*c*dd*ff-bb*c*e*ff+2*b*cc*e*ff-b*c*ee*ff; double k5=-cc*e*ee*f+c*ee*ee*f+cc*cc*f*f+cc*e*e*ff-c*e*ee*ff-2*c*cc*f*ff+c*c*ff*ff; double u1=k2/(4*k1); double u2=k2*k2/(4*k1*k1)-2*k3/(3*k1); double u3=k2*k2/(2*k1*k1)-4*k3/(3*k1); double u4=-k2*k2*k2/(k1*k1*k1)+4*k2*k3/(k1*k1)-8*k4/k1; double p1=k3*k3-3*k2*k4+12*k1*k5; double p2=2*k3*k3*k3-9*k2*k3*k4+27*k1*k4*k4+27*k2*k2*k5-72*k1*k3*k5; // System.out.println("*************"); // System.out.println("k1="+k1); // System.out.println("k2="+k2); // System.out.println("k3="+k3); // System.out.println("k4="+k4); // System.out.println("k5="+k5); Complex q1=new Complex(-4*p1*p1*p1+p2*p2).sqrt(); q1=Complex.plus(q1, new Complex(p2, 0)); q1=q1.sqrt3(); // System.out.println("q1="+q1.real()+" + "+q1.img()+" I"); Complex r1=Complex.div(new Complex(cub2*p1), Complex.mult(new Complex(3*k1), q1)); r1=Complex.plus(r1, Complex.div(q1, new Complex(3*cub2*k1))); Complex sa=Complex.plus(new Complex(u2), r1); sa=Complex.div(sa.sqrt(), 2); Complex sb=Complex.minus(new Complex(u3), r1); sb=Complex.minus(sb, Complex.div(new Complex(u4), Complex.mult(8, sa))); sb=Complex.div(sb.sqrt(), 2); Complex sc=Complex.minus(new Complex(u3), r1); sc=Complex.plus(sc, Complex.div(new Complex(u4), Complex.mult(8, sa))); sc=Complex.div(sc.sqrt(), 2); Complex[] X=new Complex[4]; Complex cu1=new Complex(-u1); X[0]=Complex.minus(cu1, sa); X[0]=Complex.minus(X[0], sb); X[1]=Complex.minus(cu1, sa); X[1]=Complex.plus(X[1], sb); X[2]=Complex.plus(cu1, sa); X[2]=Complex.minus(X[2], sc); X[3]=Complex.plus(cu1, sa); X[3]=Complex.plus(X[3], sc); // System.out.println("*************"); // System.out.println("x1 = "+X[0].real()+" + "+X[0].img()+" I"); // System.out.println("x2 = "+X[1].real()+" + "+X[1].img()+" I"); // System.out.println("x3 = "+X[2].real()+" + "+X[2].img()+" I"); // System.out.println("x4 = "+X[3].real()+" + "+X[3].img()+" I"); // Recherche des ordonnées des points d'intersection : double A=c, B, C, AA=cc, BB, CC; for (int i=0; i<4; i++) { if (Math.abs(X[i].img())>1E-10) { points.add(new CoordinatesXY()); } else { B=b*X[i].real()+e; C=a*X[i].real()*X[i].real()+d*X[i].real()+f; BB=bb*X[i].real()+ee; CC=aa*X[i].real()*X[i].real()+dd*X[i].real()+ff; double denom=A*BB-B*AA; if (Math.abs(denom)<1E-20) { points.add(new CoordinatesXY()); } else { double y=(C*AA-A*CC)/denom;//formula by Dominique Tournès points.add(new CoordinatesXY(X[i].real(), y)); } // System.out.println("*************"); // System.out.println("x["+i+"]="+X[i].real()); // System.out.println("y["+i+"]="+y); // System.out.println("A*BB-B*AA="+(A*BB-B*AA)); } } // System.out.println("*************"); // for (int i=0;i=5) // Schema schon fertig { colindex[c]=-1; continue; } // Berechne Pivotelement mit spaltenweiser Maximumssuche double max=Math.abs(A[r][c]); int imax=r; for (int i=r+1; i<5; i++) { final double h=Math.abs(A[i][c]); if (h>max) { max=h; imax=i; } } if (max>1e-13) { // Vertausche Zeilen: if (imax!=r) { final double h[]=A[imax]; A[imax]=A[r]; A[r]=h; } // Mache restliche Spalte zu 0: for (int i=r+1; i<5; i++) { final double lambda=A[i][c]/A[r][c]; for (int j=c+1; j<6; j++) { A[i][j]-=lambda*A[r][j]; } } colindex[c]=r; r++; } else { colindex[c]=-1; } } // Berechne die x-Werte: X=new double[6]; for (int j=5; j>=0; j--) { if (colindex[j]<0) { X[j]=1; } else { double h=0; final int i=colindex[j]; for (int k=j+1; k<6; k++) { h+=A[i][k]*X[k]; } X[j]=-h/A[i][j]; } } // Normalisiere double sum=0; for (int i=0; i<=5; i++) { sum+=Math.abs(X[i]); } if (sum<1e-10) { Valid=false; } for (int i=0; i<=5; i++) { X[i]/=sum; // Ce qui suit ressemble à un gag, pourtant il semble que l'epsilon au lieu de 0 en coeffs permet // de surmonter les effets de bord dans des cas particuliers (ex. hyperbole equilatère/parabole) // sans pour autant porter atteinte à la précision des coordonnées des points d'intersections // qui restent fiables à 1e-12, soit la précision maximale affichée du logiciel : X[i]=n(X[i]); } } private static double n(double x) { if (Math.abs(x)<1E-16) { double sign=(Math.signum(x)<0)?-1:1; return 1.0E-16*sign; } return x; } @Override public void paint(final MyGraphics g, final ZirkelCanvas zc) { if (!Valid||mustHide(zc)) { return; } g.setColor(this); // Draw the lower part of the quadrik (minus the root): final double start=zc.minX(); double x=start; final double end=zc.maxX(); final double h=zc.dx(zc.getOne()); boolean valid=false, ptext=false; double c0=0, r0=0; double ctext=20, rtext=20; final PolygonDrawer pd=new PolygonDrawer(true,g, this); // Draw the lower part of the quadric (plus the root): while (x<=end) { try { final double y=computeLower(x); final double c=zc.col(x), r=zc.row(y); if (valid) {//en ajoutant r>0, la ligne verticale disparaissait; elle est réapparue! pd.drawTo(c, r); if (!ptext&&r0-r>c-c0&&zc.isInside(x, y)) { ctext=c; rtext=r; ptext=true; } } else { pd.startPolygon(c, r); } c0=c; r0=r; valid=true; } catch (final RuntimeException e) { valid=false; } x+=h; } pd.finishPolygon(); // Draw the upper part of the quadric (plus the root): x=start-2*h; valid=false; while (x<=end+2*h) { try { final double y=computeUpper(x); final double c=zc.col(x), r=zc.row(y); if (valid) { pd.drawTo(c, r); // Try to find a position for the label: if (!ptext&&r0-r>c-c0&&zc.isInside(x, y)) { ctext=c; rtext=r; ptext=true; } } else // left edge of quadric, connect with lower part { try { final double y1=computeLower(x); if (x>=start-h&&x<=end+h) { g.drawLine(c, zc.row(y1), c, r, this); } } catch (final RuntimeException e) { } pd.startPolygon(c, r); } c0=c; r0=r; valid=true; } catch (final RuntimeException e) // no points in that range { if (valid) // we just left the right edge of the quadric { try { final double y1=computeLower(x-h); if (x-h>=start-h&&x-h<=end+h) { g.drawLine(c0, zc.row(y1), c0, r0, this); } } catch (final RuntimeException ex) { } } valid=false; } x+=h; } pd.finishPolygon(); final String s=getDisplayText(); if (!s.equals("")) { g.setLabelColor(this); setFont(g); DisplaysText=true; TX1=ctext+zc.col(XcOffset)-zc.col(0); TY1=rtext+zc.row(YcOffset)-zc.row(0); drawLabel(g, s); } } static public final String Tags[]={"x^2", "y^2", "x", "y", "xy"}; // public String getDisplayValue() { // String s=""; // for (int i=0; i<5; i++) { // s=s+helpDisplayValue(i==0, -X[i], Tags[i]); // } // return s+"="+roundDisplay(X[5]); // } @Override public String getDisplayValue() { String s=""; s+="("+Global.getLocaleNumber(-X[0], "length"); s+="*"; s+=Tags[0]+")"; for (int i=1; i<5; i++) { s+="+"; s+="("+Global.getLocaleNumber(-X[i], "length"); s+="*"; s+=Tags[i]+")"; // s=s+helpDisplayValue(i==0, -X[i], Tags[i]); } return s+"="+Global.getLocaleNumber(X[5], "length"); // return s+"="+roundDisplay(X[5]); } @Override public String getEquation() { return getDisplayValue(); } @Override public boolean nearto(final int cc, final int rr, final ZirkelCanvas zc) { if (!displays(zc)) { return false; } final int size=(int) zc.selectionSize(); final double start=zc.minX(); double x=start; final double end=zc.maxX(); final double h=zc.dx(zc.getOne()); while (x<=end) { try { final double y=computeUpper(x); final double c=zc.col(x), r=zc.row(y); if (Math.abs(cc-c)<=size*3/2 &&Math.abs(rr-r)<=size*3/2) { return true; } } catch (final Exception e) { } try { final double y=computeLower(x); final double c=zc.col(x), r=zc.row(y); if (Math.abs(cc-c)<=size*3/2 &&Math.abs(rr-r)<=size*3/2) { return true; } } catch (final Exception e) { } x+=h; } return false; } public double computeUpper(final double x) { if (Math.abs(X[1])>1e-13) { final double p=(X[3]+x*X[4])/X[1], q=(X[0]*x*x+X[2] *x+X[5]) /X[1]; final double h=p*p/4-q; if (h<0) { throw new RuntimeException(""); } return -p/2+Math.sqrt(h); } else { return -(X[0]*x*x+X[2]*x+X[5])/(X[3]+X[4]*x); } } public double computeLower(final double x) { if (Math.abs(X[1])>1e-13) { final double p=(X[3]+x*X[4])/X[1], q=(X[0]*x*x+X[2] *x+X[5]) /X[1]; final double h=p*p/4-q; if (h<0) { throw new RuntimeException(""); } return -p/2-Math.sqrt(h); } else { throw new RuntimeException(""); } } @Override public void printArgs(final XmlWriter xml) { for (int i=0; i