660 lines
19 KiB
Java
660 lines
19 KiB
Java
/*
|
|
|
|
Copyright 2006 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;
|
|
|
|
import java.util.Enumeration;
|
|
import java.util.Vector;
|
|
import rene.gui.Global;
|
|
import rene.zirkel.ZirkelCanvas;
|
|
import rene.zirkel.construction.Construction;
|
|
import rene.zirkel.graphics.MyGraphics;
|
|
import rene.zirkel.graphics.PolygonDrawer;
|
|
import rene.zirkel.structures.Coordinates;
|
|
|
|
/**
|
|
*
|
|
* @author erichake
|
|
*/
|
|
public class JLocusTrackObject extends TrackObject implements HeavyObject {
|
|
|
|
Coordinates C;
|
|
PolygonDrawer pd;
|
|
double c0, r0, c, r;
|
|
ExpressionObject EO;
|
|
Vector RefreshList=new Vector();
|
|
Vector DriverObjectList=new Vector();
|
|
boolean Special=false;
|
|
double cx, cy, ww, wh;
|
|
long time=0;
|
|
|
|
public JLocusTrackObject(final Construction c, final ConstructionObject p,
|
|
final ConstructionObject po[], final int pn,
|
|
final ConstructionObject o, final PointObject pm) {
|
|
|
|
super(c);
|
|
P=p;
|
|
PN=pn;
|
|
for (int i=0; i<PN; i++) {
|
|
PO[i]=po[i];
|
|
}
|
|
O=o;
|
|
PM=pm;
|
|
validate();
|
|
updateText();
|
|
cx=c.getX();
|
|
cy=c.getY();
|
|
ww=c.getW();
|
|
wh=c.getH();
|
|
searchDependencies(c);
|
|
|
|
}
|
|
|
|
@Override
|
|
public void searchDependencies(final Construction c) {
|
|
RefreshList.clear();
|
|
DriverObjectList.clear();
|
|
if (O instanceof SegmentObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof ExpressionObject) {
|
|
searchDependencies(c, this, null);
|
|
} else if (O instanceof RayObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof PrimitiveLineObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof PrimitiveCircleObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof QuadricObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof TrackObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof AreaObject) {
|
|
searchDependencies(c, this, PM);
|
|
} else if (O instanceof FunctionObject) {
|
|
searchDependencies(c, this, PM);
|
|
}
|
|
NeedsRecompute=true;
|
|
}
|
|
|
|
public void searchDependencies(final Construction c,
|
|
final ConstructionObject o, final ConstructionObject avoid) {
|
|
|
|
if (o.RekValidating) {
|
|
return;
|
|
} // should not happen!
|
|
o.RekValidating=true;
|
|
Enumeration e=c.elements();
|
|
while (e.hasMoreElements()) {
|
|
((ConstructionObject) e.nextElement()).setRekFlag(false);
|
|
}
|
|
recursiveSearchDependencies(o, avoid);
|
|
e=c.elements();
|
|
|
|
while (e.hasMoreElements()) {
|
|
final ConstructionObject oc=(ConstructionObject) e.nextElement();
|
|
if (oc.isRekFlag()) {
|
|
RefreshList.addElement(oc);
|
|
if ((!(oc==P))&&(oc.isDriverObject())) {
|
|
DriverObjectList.addElement(oc);
|
|
}
|
|
}
|
|
}
|
|
|
|
o.RekValidating=false;
|
|
}
|
|
|
|
public void recursiveSearchDependencies(final ConstructionObject o,
|
|
final ConstructionObject avoid) {
|
|
|
|
if (o.isRekFlag()||o==avoid) {
|
|
return;
|
|
}
|
|
o.setRekFlag(true);
|
|
final ConstructionObject d[]=o.getDepArray();
|
|
for (final ConstructionObject element : d) {
|
|
recursiveSearchDependencies(element, avoid);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean needsToRecompute() {
|
|
|
|
boolean needs=false;
|
|
final Enumeration pl=DriverObjectList.elements();
|
|
while (pl.hasMoreElements()) {
|
|
final DriverObject oc=(DriverObject) pl.nextElement();
|
|
if (oc.somethingChanged()) {
|
|
Global.addClearList(oc);
|
|
// ConstructionObject oo=(ConstructionObject) oc;
|
|
// System.out.println("needsRecompute :"+oo.getName());
|
|
needs=true;
|
|
}
|
|
}
|
|
|
|
if ((Cn.getX()!=cx)||(Cn.getY()!=cy)||(Cn.getW()!=ww)
|
|
||(Cn.getH()!=wh)) {
|
|
cx=Cn.getX();
|
|
cy=Cn.getY();
|
|
ww=Cn.getW();
|
|
wh=Cn.getH();
|
|
needs=true;
|
|
}
|
|
|
|
if (NeedsRecompute) {
|
|
NeedsRecompute=false;
|
|
return true;
|
|
}
|
|
|
|
return needs;
|
|
}
|
|
|
|
public synchronized void refresh() {
|
|
final Enumeration e=RefreshList.elements();
|
|
while (e.hasMoreElements()) {
|
|
final ConstructionObject oc=(ConstructionObject) e.nextElement();
|
|
oc.NeedsRecompute=true;
|
|
oc.validate();
|
|
}
|
|
}
|
|
private static double a=5, b=6, xmin=-b+0.0001;
|
|
|
|
private static void setConstants(final Construction c) {
|
|
final double dx=c.getW()*2;
|
|
final double dy=c.getH();
|
|
final double r=Math.sqrt(dx*dx+dy*dy);
|
|
a=r;
|
|
b=1.5*a;
|
|
xmin=-b+(b-a)/1000;
|
|
}
|
|
|
|
public static double fline(final double x) {
|
|
if (Math.abs(x)<a) {
|
|
return x;
|
|
} else {
|
|
final double s=Math.signum(x);
|
|
return s*(2*a-b)+((b-a)*(b-a))/(s*b-x);
|
|
}
|
|
}
|
|
private Coordinates oldC=new Coordinates();
|
|
|
|
@Override
|
|
public void addCoordinates(final Vector v, final ConstructionObject p) {
|
|
if (p.valid()) {
|
|
oldC=new Coordinates(p.getX(), p.getY());
|
|
v.addElement(oldC);
|
|
} else {
|
|
if (!Double.isNaN(oldC.X)) {
|
|
oldC=new Coordinates();
|
|
v.addElement(oldC);
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized void docomputeCircle() {
|
|
double dt;
|
|
double a1;
|
|
|
|
final PrimitiveCircleObject CO=(PrimitiveCircleObject) O;
|
|
final double x=CO.getX(), y=CO.getY(), RO=CO.getR();
|
|
|
|
if (CO.hasRange()) {
|
|
a1=CO.getA1();
|
|
final double a2=CO.getA2();
|
|
double d=a2-a1;
|
|
while (d<0) {
|
|
d+=2*Math.PI;
|
|
}
|
|
while (d>=2*Math.PI) {
|
|
d-=2*Math.PI;
|
|
}
|
|
dt=d*DMin;
|
|
// I really don't understand why this is necessary for arc locuses :
|
|
a1+=0.00000000001;
|
|
} else {
|
|
dt=2*Math.PI*DMin;
|
|
a1=0;
|
|
}
|
|
|
|
final int nbsteps=(int) Math.round(1/DMin)+1;
|
|
for (int i=0; i<nbsteps; i++) {
|
|
PM.move(x+RO*Math.cos(a1+i*dt), y+RO
|
|
*Math.sin(a1+i*dt));
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
|
|
// Try to do something with points inside circle... I will see later !
|
|
|
|
// if (PM.isInside()) {
|
|
// Special=true;
|
|
// double rvar=r/nbsteps;
|
|
// r=0;
|
|
// for (int i=0; i<nbsteps; i++) {
|
|
// PM.move(x+r*Math.cos(a1+i*dt), y+r*Math.sin(a1+i*dt));
|
|
// refresh();
|
|
// if (P.valid()) {
|
|
// addCoordinates(V, P);
|
|
// }
|
|
// r+=rvar;
|
|
// }
|
|
// } else {
|
|
//
|
|
// for (int i=0; i<nbsteps; i++) {
|
|
// PM.move(x+r*Math.cos(a1+i*dt), y+r*Math.sin(a1+i*dt));
|
|
// refresh();
|
|
// if (P.valid()) {
|
|
// addCoordinates(V, P);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
}
|
|
|
|
public synchronized void docomputeLine() {
|
|
Cn.shouldSwitch(false);
|
|
final PrimitiveLineObject l=(PrimitiveLineObject) O;
|
|
// System.out.println("zc.DX="+zc.DX+" zc.DY="+zc.DY);
|
|
// System.out.println("dx="+Cn.getW()*2+" dy="+Cn.getH());
|
|
setConstants(Cn);
|
|
final double da=-2*xmin*DMin;
|
|
final double lx=l.getX(), ly=l.getY(), ldx=l.getDX(), ldy=l.getDY();
|
|
double delta;
|
|
final int nbsteps=(int) Math.round(1/DMin)+1;
|
|
for (int i=0; i<nbsteps; i++) {
|
|
delta=fline(xmin+i*da);
|
|
PM.move(lx+ldx*delta, ly+ldy*delta);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
}
|
|
|
|
public synchronized void docomputeRay() {
|
|
Cn.shouldSwitch(false);
|
|
final PrimitiveLineObject l=(PrimitiveLineObject) O;
|
|
|
|
setConstants(Cn);
|
|
final double da=-xmin*DMin;
|
|
final double lx=l.getX(), ly=l.getY(), ldx=l.getDX(), ldy=l.getDY();
|
|
double delta;
|
|
final int nbsteps=(int) Math.round(1/DMin)+1;
|
|
for (int i=0; i<nbsteps; i++) {
|
|
delta=fline(i*da);
|
|
PM.move(lx+ldx*delta, ly+ldy*delta);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
}
|
|
|
|
public synchronized void docomputeSegments() {
|
|
Cn.shouldSwitch(false);
|
|
final PrimitiveLineObject l=(PrimitiveLineObject) O;
|
|
final double r=((SegmentObject) l).getLength();
|
|
final double lx=l.getX(), ly=l.getY(), ldx=r*l.getDX()*DMin, ldy=r
|
|
*l.getDY()*DMin;
|
|
|
|
|
|
final int nbsteps=(int) Math.round(1/(DMin))+1;
|
|
|
|
for (int i=0; i<nbsteps; i++) {
|
|
PM.move(lx+ldx*i, ly+ldy*i);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute the locus of a point on conic The idea is to consider the first
|
|
* point A of the conic (first of five points) then imagine a unit circle
|
|
* centered on A, imagine a point B on this circle, then compute
|
|
* intersections between line (AB) and the conic. This will give continuous
|
|
* points set on the conic, then refresh to get the locus point.
|
|
*
|
|
* @param zc
|
|
*/
|
|
public synchronized void docomputeQuadric() {
|
|
Cn.shouldSwitch(false);
|
|
final QuadricObject q=(QuadricObject) O;
|
|
|
|
final double A=q.X[0], B=q.X[1], C=q.X[2], D=q.X[3], E=q.X[4], F=q.X[5];
|
|
final double dt=Math.PI*DMin;
|
|
// get the first of the five points defining the conic :
|
|
final double X1=q.P[0].getX();
|
|
final double Y1=q.P[0].getY();
|
|
|
|
double M=0, N2=0, P1=0;
|
|
final int nbsteps=(int) Math.round(1/DMin)+1;
|
|
// System.out.println("*******************");
|
|
for (int i=0; i<nbsteps; i++) {
|
|
M=-Math.sin(i*dt+0.01);
|
|
N2=Math.cos(i*dt+0.01);
|
|
P1=-(M*X1+N2*Y1);
|
|
|
|
double x1=0, x2=0, y1=0, y2=0;
|
|
final double part1=-2*B*M*P1-C*N2*N2+D*M*N2+E
|
|
*N2*P1;
|
|
final double part2=Math.abs(N2)
|
|
*Math.sqrt(-2*M*D*N2*C+4*P1*D*A*N2+4
|
|
*P1*M*B*C+4*E*M*N2*F-2*E*P1
|
|
*N2*C-2*E*P1*M*D-4*M*M*B*F
|
|
-4*P1*P1*A*B-4*A*N2*N2*F+N2
|
|
*N2*C*C+M*M*D*D+E*E*P1*P1);
|
|
|
|
final double part3=2*A*N2*N2+2*B*M*M+(-2*E)*M
|
|
*N2;
|
|
x1=(part1+part2)/part3;
|
|
y1=(-M*x1-P1)/N2;
|
|
x2=(part1-part2)/part3;
|
|
y2=(-M*x2-P1)/N2;
|
|
|
|
if (((x2-x1)/N2)<0) {
|
|
final double c1=x1, r1=y1;
|
|
x1=x2;
|
|
y1=y2;
|
|
x2=c1;
|
|
y2=r1;
|
|
}
|
|
|
|
if ((Math.abs(X1-x1)<1e-10)&&(Math.abs(X1-x1)<1e-10)) {
|
|
PM.move(x2, y2);
|
|
} else {
|
|
PM.move(x1, y1);
|
|
}
|
|
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
|
|
}
|
|
|
|
public synchronized void docomputeExpression() {
|
|
Cn.shouldSwitch(false);
|
|
EO=(ExpressionObject) O;
|
|
if (!EO.isSlider()) {
|
|
return;
|
|
}
|
|
final int nbsteps=(int) Math.round(1/DMin)+1;
|
|
double oldCursor=EO.getSliderPosition();
|
|
for (int i=0; i<nbsteps; i++) {
|
|
EO.setSliderPosition(DMin*i);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
EO.setSliderPosition(oldCursor);
|
|
}
|
|
|
|
public synchronized void docomputeTrack() {
|
|
Cn.shouldSwitch(false);
|
|
final JLocusTrackObject TR=(JLocusTrackObject) O;
|
|
if (DMin!=TR.DMin) {
|
|
TR.DMin=DMin;
|
|
TR.compute();
|
|
}
|
|
final Enumeration e=TR.V.elements();
|
|
while (e.hasMoreElements()) {
|
|
C=(Coordinates) e.nextElement();
|
|
PM.move(C.X, C.Y);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
}
|
|
|
|
public synchronized void docomputeFunction() {
|
|
Cn.shouldSwitch(false);
|
|
final FunctionObject FO=(FunctionObject) O;
|
|
final Enumeration e=FO.V.elements();
|
|
while (e.hasMoreElements()) {
|
|
C=(Coordinates) e.nextElement();
|
|
PM.move(C.X, C.Y);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
}
|
|
|
|
public synchronized void docomputeArea() {
|
|
Cn.shouldSwitch(false);
|
|
final AreaObject poly=(AreaObject) O;
|
|
final int NVertex=poly.V.size();
|
|
int nbsteps=(int) Math.round(1/(DMin*NVertex));
|
|
if (nbsteps<2) {
|
|
nbsteps=2;
|
|
}
|
|
double xA, xB, yA, yB;
|
|
final PointObject ORIGIN=(PointObject) poly.V.get(0);
|
|
PointObject A=ORIGIN;
|
|
PointObject B=null;
|
|
for (int n=1; n<NVertex; n++) {
|
|
B=(PointObject) poly.V.get(n);
|
|
xA=A.getX();
|
|
yA=A.getY();
|
|
xB=B.getX();
|
|
yB=B.getY();
|
|
for (int i=0; i<=nbsteps; i++) {
|
|
PM.move(xA+i*(xB-xA)/nbsteps, yA+i*(yB-yA)
|
|
/nbsteps);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
A=B;
|
|
}
|
|
xA=A.getX();
|
|
yA=A.getY();
|
|
xB=ORIGIN.getX();
|
|
yB=ORIGIN.getY();
|
|
for (int i=0; i<=nbsteps; i++) {
|
|
PM.move(xA+i*(xB-xA)/nbsteps, yA+i*(yB-yA)/nbsteps);
|
|
refresh();
|
|
addCoordinates(V, P);
|
|
}
|
|
|
|
// double r=((SegmentObject) l).getLength();
|
|
// double lx=l.getX(), ly=l.getY(), ldx=r*l.getDX()*DMin,
|
|
// ldy=r*l.getDY()*DMin;
|
|
//
|
|
// int nbsteps=(int) Math.round(1/DMin)+1;
|
|
// for (int i=0; i<nbsteps; i++) {
|
|
// PM.move(lx+ldx*i, ly+ldy*i);
|
|
// refresh();
|
|
// if (P.valid()) {
|
|
// addCoordinates(V, P);
|
|
// }
|
|
// }
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
public synchronized void compute(final ZirkelCanvas zc) {
|
|
compute();
|
|
}
|
|
|
|
public synchronized void compute() {
|
|
if (Fixed&&!StartFix) {
|
|
return;
|
|
}
|
|
|
|
// System.out.println("compute track");
|
|
|
|
V=new Vector();
|
|
oldC=new Coordinates();
|
|
StartFix=false;
|
|
double x=0, y=0;
|
|
if (PM!=null) {
|
|
x=PM.getX();
|
|
y=PM.getY();
|
|
}
|
|
|
|
Cn.clearSwitches();
|
|
Cn.shouldSwitch(true);
|
|
DontProject=true;
|
|
|
|
// Running on a segment :
|
|
if (O instanceof SegmentObject) {
|
|
docomputeSegments();
|
|
// Running on a conic :
|
|
} else if (O instanceof QuadricObject) {
|
|
docomputeQuadric();
|
|
// Running on an track :
|
|
} else if (O instanceof JLocusTrackObject) {
|
|
docomputeTrack();
|
|
// Running on an area :
|
|
} else if (O instanceof AreaObject) {
|
|
docomputeArea();
|
|
// Running on an expression :
|
|
} else if (O instanceof ExpressionObject) {
|
|
docomputeExpression();
|
|
// Running on a ray :
|
|
} else if (O instanceof RayObject) {
|
|
docomputeRay();
|
|
// Running on a circle :
|
|
} else if (O instanceof PrimitiveCircleObject) {
|
|
docomputeCircle();
|
|
// Running on a line :
|
|
} else if (O instanceof PrimitiveLineObject) {
|
|
docomputeLine();
|
|
} else if (O instanceof FunctionObject) {
|
|
docomputeFunction();
|
|
}
|
|
|
|
DontProject=false;
|
|
// zc.getConstruction().shouldSwitch(false);
|
|
// zc.getConstruction().clearSwitches();
|
|
if (PM!=null) {
|
|
PM.move(x, y);
|
|
refresh();
|
|
}
|
|
|
|
Cn.dovalidate();
|
|
|
|
// zc.dovalidate();
|
|
|
|
}
|
|
|
|
// public void validate() {
|
|
// compute();
|
|
// }
|
|
// Paint the track
|
|
@Override
|
|
public void paint(final MyGraphics g, final ZirkelCanvas zc) {
|
|
|
|
if (!Valid||mustHide(zc)) {
|
|
return;
|
|
}
|
|
Coordinates C;
|
|
final Enumeration e=V.elements();
|
|
double c, r;
|
|
if (indicated()) {
|
|
final boolean sel=P.indicated();
|
|
P.setIndicated(true);
|
|
g.setColor(P);
|
|
P.setIndicated(sel);
|
|
} else {
|
|
g.setColor(this);
|
|
}
|
|
|
|
if (Special) {
|
|
while (e.hasMoreElements()) {
|
|
C=(Coordinates) e.nextElement();
|
|
PointObject.drawPoint(g, zc, this, C.X, C.Y, Type);
|
|
}
|
|
} else {
|
|
final PolygonDrawer pd=new PolygonDrawer(false,g, this);
|
|
|
|
if (e.hasMoreElements()) {
|
|
|
|
C=(Coordinates) e.nextElement();
|
|
c0=zc.col(C.X);
|
|
r0=zc.row(C.Y);
|
|
pd.startPolygon(c0, r0);
|
|
|
|
}
|
|
|
|
while (e.hasMoreElements()) {
|
|
C=(Coordinates) e.nextElement();
|
|
c=zc.col(C.X);
|
|
r=zc.row(C.Y);
|
|
if (Math.abs(pd.c()-c)<1000&&Math.abs(pd.r()-r)<1000) {
|
|
pd.drawTo(c, r);
|
|
} else {
|
|
pd.finishPolygon();
|
|
pd.startPolygon(c, r);
|
|
}
|
|
}
|
|
|
|
pd.finishPolygon();
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public boolean isSpecial() {
|
|
return Special;
|
|
}
|
|
|
|
@Override
|
|
public void setSpecial(final boolean f) {
|
|
Special=f;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(final ConstructionObject o) {
|
|
if (!(o instanceof JLocusTrackObject)||!o.valid()) {
|
|
return false;
|
|
}
|
|
JLocusTrackObject loc=(JLocusTrackObject) o;
|
|
if (V.size()!=loc.V.size()) {
|
|
return false;
|
|
}
|
|
final Enumeration e1=V.elements();
|
|
final Enumeration e2=loc.V.elements();
|
|
while (e1.hasMoreElements()) {
|
|
Coordinates C1=(Coordinates) e1.nextElement();
|
|
Coordinates C2=(Coordinates) e2.nextElement();
|
|
if ((Math.abs(C1.X-C2.X)>1e-8)||Math.abs(C1.Y-C2.Y)>1e-8) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public ConstructionObject copy(final double x, final double y) {
|
|
PointObject newPM=null;
|
|
if (PM!=null) {
|
|
newPM=(PointObject) PM.getTranslation();
|
|
}
|
|
|
|
final JLocusTrackObject jl=new JLocusTrackObject(Cn.getTranslation(),
|
|
P.getTranslation(), PO, PN, O.getTranslation(), newPM);
|
|
jl.setTargetDefaults();
|
|
return jl;
|
|
}
|
|
// public void translate() {
|
|
// if (PM!=null) {
|
|
// PM=(PointObject) PM.getTranslation();
|
|
// }
|
|
//
|
|
// O=O.getTranslation();
|
|
// P=P.getTranslation();
|
|
//
|
|
// searchDependencies(Cn.getTranslation());
|
|
// }
|
|
}
|