830 lines
27 KiB
Java
830 lines
27 KiB
Java
/*
|
||
|
||
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;
|
||
}
|
||
}
|