CaRMtl/rene/zirkel/objects/QuadricObject.java

830 lines
27 KiB
Java
Raw Normal View History

/*
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 <http://www.gnu.org/licenses/>.
*/
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<P.length; i++) {
Names[i]=P[i].getName();
}
if (P[0].is3D()&&P[1].is3D()&&P[2].is3D()&&P[3].is3D()&&P[4].is3D()) setText(Global.name("text.circle3D"));
else setText(textAny(Global.name("text.quadric"), Names));
} catch (final Exception e) {
}
}
private static final double cub2=Math.pow(2, 1.0/3.0);
public static ArrayList<CoordinatesXY> intersect(final QuadricObject quad1,
final QuadricObject quad2) {
ArrayList<CoordinatesXY> points=new ArrayList<CoordinatesXY>();
// 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<points.size();i++){
// CoordinatesXY coords=points.get(i);
// System.out.println("point "+i+" : "+coords.X+" "+coords.Y);
// }
return points;
}
@Override
public void validate() {
for (int i=0; i<P.length; i++) {
if (!P[i].valid()) {
Valid=false;
return;
}
}
Valid=true;
// Baue Koeffizientenmatrix auf (x^2,y^2,x,y,xy,1):
final double A[][]=new double[5][6];
for (int i=0; i<5; i++) {
final double x=P[i].getX(), y=P[i].getY();
A[i][0]=x*x;
A[i][1]=y*y;
A[i][2]=x;
A[i][3]=y;
A[i][4]=x*y;
A[i][5]=1;
double sum=0;
for (int j=0; j<6; j++) {
sum+=A[i][j]*A[i][j];
}
sum=Math.sqrt(sum);
for (int j=0; j<6; j++) {
A[i][j]/=sum;
}
}
// Gauflverfahren, um auf untere Dreiecksmatrix zu kommen
int r=0;
final int colindex[]=new int[6]; // Index der Stufe oder -1 (keine
// Stufe)
// Iteration ¸ber alle Spalten:
for (int c=0; c<6; c++) {
if (r>=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<P.length; i++) {
xml.printArg("point"+(i+1), P[i].getName());
}
}
@Override
public Enumeration secondaryParams() {
DL.reset();
for (final PointObject element : P) {
DL.add(element);
}
return DL.elements();
}
@Override
public Enumeration depending() {
DL.reset();
for (final PointObject element : P) {
// System.out.println(element.getName());
DL.add(element);
}
return DL.elements();
}
@Override
public void translate() {
for (int i=0; i<P.length; i++) {
P[i]=(PointObject) P[i].getTranslation();
}
}
@Override
public ConstructionObject copy(final double x, final double y) {
try {
final QuadricObject o=(QuadricObject) clone();
setTranslation(o);
o.P=new PointObject[P.length];
for (int i=0; i<P.length; i++) {
o.P[i]=P[i];
}
o.translateConditionals();
o.translate();
o.setName();
o.updateText();
o.setBreak(false);
// o.setTarget(false); Dibs
return o;
} catch (final Exception e) {
return null;
}
}
@Override
public boolean onlynearto(final int x, final int y, final ZirkelCanvas zc) {
return false;
}
@Override
public boolean equals(final ConstructionObject o) {
if (!(o instanceof QuadricObject)||!o.valid()) {
return false;
}
try {
for (int i=0; i<6; i++) {
if (!equals(X[i], ((QuadricObject) o).X[i])) {
return false;
}
}
} catch (final RuntimeException e) {
return false;
}
return true;
}
@Override
public boolean hasUnit() {
return false;
}
public void keepBaricentricCoords(final PointObject P) {
if (!P.isPointOn()) {
return;
}
if (P.BarycentricCoordsInitialzed) {
final PointObject AA=this.P[0];
final PointObject BB=this.P[1];
final PointObject CC=this.P[2];
final double xa=AA.getX(), ya=AA.getY();
final double xb=BB.getX(), yb=BB.getY();
final double xc=CC.getX(), yc=CC.getY();
final double xm=xa+P.Gx*(xb-xa)+P.Gy*(xc-xa);
final double ym=ya+P.Gx*(yb-ya)+P.Gy*(yc-ya);
P.move(xm, ym);
} else {
P.computeBarycentricCoords();
}
}
@Override
public int getDistance(final PointObject P) {
final double a=X[0], b=X[1], c=X[2], d=X[3], e=X[4], r=X[5];
final double xc=P.getX(), yc=P.getY();
if (Math.abs(a*xc*xc+b*yc*yc+c*xc+d*yc+e*xc*yc
+r)<1e-13) // close enough
{
return 0;
}
final double t[]=new double[5], s[]=new double[5];
// Coefficients for fourth order polynomial for lambda (Lagrange factor)
// Minimize (x-xc)^2+(y-yc)^2 with a*x^2+b*y^2+c*x+d*y+e*x*y+r=0
// Computed with Maple
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-c
*Math.pow(e, 3)*d+r*Math.pow(e, 4)-8*r*e*e*b
*a+16*r*b*b*a*a;
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+8*b
*a*d*d+8*r*e*e*a-32*r*b*b*a-32*r
*b*a*a;
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
-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-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-4*b*xc*xc*e*e+4*Math.pow(e, 3)*yc*xc
+64*r*b*a-16*a*b*xc*e*yc;
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-32*b*xc
*c+16*e*yc*c-32*a*d*yc-32*a*b*xc*xc
-32*b*a*yc*yc;
t[4]=16*b*yc*yc+16*d*yc+16*c*xc+16*xc*e*yc
+16*r+16*a*xc*xc;
final int k=Quartic.solve(t, s);
// System.out.println(k+"Solutions found.");
double dmin=1e30, xmin=xc, ymin=yc;
for (int i=0; i<k; i++) // Choose closest solution of Lagrange
// equation
{
final double l=s[i];
// Solve for x,y when lambda is known.
// Computed with Maple
final 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);
final 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);
final double dist=(px-xc)*(px-xc)+(py-yc)*(py-yc);
if (dist<dmin) {
dmin=dist;
xmin=px;
ymin=py;
}
}
final double dd=Math.sqrt((P.getX()-xmin)*(P.getX()-xmin)
+(P.getY()-ymin)*(P.getY()-ymin));
return (int) Math.round(dd*Cn.getPixel());
}
public void project(final PointObject P) {
keepBaricentricCoords(P);
final double a=X[0], b=X[1], c=X[2], d=X[3], e=X[4], r=X[5];
final double xc=P.getX(), yc=P.getY();
if (Math.abs(a*xc*xc+b*yc*yc+c*xc+d*yc+e*xc*yc
+r)<1e-13) // close enough
{
return;
}
final double t[]=new double[5], s[]=new double[5];
// Coefficients for fourth order polynomial for lambda (Lagrange factor)
// Minimize (x-xc)^2+(y-yc)^2 with a*x^2+b*y^2+c*x+d*y+e*x*y+r=0
// Computed with Maple
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-c
*Math.pow(e, 3)*d+r*Math.pow(e, 4)-8*r*e*e*b
*a+16*r*b*b*a*a;
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+8*b
*a*d*d+8*r*e*e*a-32*r*b*b*a-32*r
*b*a*a;
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
-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-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-4*b*xc*xc*e*e+4*Math.pow(e, 3)*yc*xc
+64*r*b*a-16*a*b*xc*e*yc;
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-32*b*xc
*c+16*e*yc*c-32*a*d*yc-32*a*b*xc*xc
-32*b*a*yc*yc;
t[4]=16*b*yc*yc+16*d*yc+16*c*xc+16*xc*e*yc
+16*r+16*a*xc*xc;
final int k=Quartic.solve(t, s);
// System.out.println(k+"Solutions found.");
double dmin=1e30, xmin=xc, ymin=yc;
for (int i=0; i<k; i++) // Choose closest solution of Lagrange
// equation
{
final double l=s[i];
// Solve for x,y when lambda is known.
// Computed with Maple
final 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);
final 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);
final double dist=(px-xc)*(px-xc)+(py-yc)*(py-yc);
if (dist<dmin) {
dmin=dist;
xmin=px;
ymin=py;
}
}
P.move(xmin, ymin);
}
public void project(final PointObject P, final double alpha) {
project(P);
}
public void dragTo(final double x, final double y) {
for (int i=0; i<5; i++) {
P[i].move(xd[i]+(x-x1), yd[i]+(y-y1));
}
}
@Override
public void move(final double x, final double y) {
}
public boolean moveable() {
for (int i=0; i<5; i++) {
if (!P[i].moveable()) {
return false;
}
}
return true;
}
double xd[], yd[], x1, y1;
public void startDrag(final double x, final double y) {
if (xd==null) {
xd=new double[5];
yd=new double[5];
}
for (int i=0; i<5; i++) {
xd[i]=P[i].getX();
yd[i]=P[i].getY();
}
x1=x;
y1=y;
}
public double getOldX() {
return 0;
}
public double getOldY() {
return 0;
}
@Override
public void snap(final ZirkelCanvas zc) {
if (moveable()) {
for (int i=0; i<5; i++) {
P[i].snap(zc);
}
}
}
public boolean canInteresectWith(final ConstructionObject o) {
return true;
}
public void repulse(final PointObject P) {
project(P);
}
public PointObject[] getP() {
return P;
}
}