CaRMtl/rene/zirkel/objects/QuadricObject.java

830 lines
27 KiB
Java
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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