/*
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