CaRMtl/rene/zirkel/objects/FunctionObject.java
2018-09-04 22:51:42 -04:00

1402 lines
35 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: Functionbject.java
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import rene.gui.Global;
import rene.util.xml.XmlWriter;
import rene.zirkel.ZirkelCanvas;
import rene.zirkel.construction.Construction;
import rene.zirkel.construction.ConstructionException;
import rene.zirkel.expression.Expression;
import rene.zirkel.expression.ExpressionColor;
import rene.zirkel.expression.InvalidException;
import rene.zirkel.graphics.FunctionPolygonFiller;
import rene.zirkel.graphics.MyGraphics;
import rene.zirkel.graphics.PolygonDrawer;
import rene.zirkel.structures.Coordinates;
/**
* @author Rene
*
* Function objects are parametric or cartesian curves. For parametric
* functions, depending on a parameter t , x(t) and y(t) are computed
* and drawn on the screen. For cartesian functions, the parameter x
* will be used.
*
*/
public class FunctionObject extends ConstructionObject implements
PointonObject, HeavyObject, DriverObject, Evaluator {
Expression EX = null, EY = null;
public Expression VarMin = null, VarMax = null, DVar = null;
String LASTEX = "", LASTEY = "", LASTVarMin = "", LASTVarMax = "", LASTDVar = "";
double X[] = { 0 };
public String Var[] = { "x" };
boolean Filled = false;
Expression Center = null;
protected int Type = 0;
public final static int SQUARE = 0, DIAMOND = 1, CIRCLE = 2, DOT = 3,
CROSS = 4, DCROSS = 5;
protected boolean Special = false;
// Vector RefreshList=new Vector();
public Vector V = new Vector();
double cx, cy, ww, wh;
// this is a main object list which tells if the object needs to recompute :
Vector DriverObjectList = new Vector();
double c0, r0, c, r;
private boolean isMinClosed, isMinOpen, isMaxClosed, isMaxOpen;
/**
* Functions are HeavyObjects which means we always must see if it really
* neads to recompute. Computing a function can be time consuming, but once
* it's computed, the paint method is fast enough.
*
* @param c
*/
public FunctionObject(final Construction c) {
super(c);
VarMin = new Expression("windowcx-windoww", c, this);
VarMax = new Expression("windowcx+windoww", c, this);
DVar = new Expression("0", c, this);
validate();
updateText();
cx = c.getX();
cy = c.getY();
ww = c.getW();
wh = c.getH();
Type = CROSS;
}
@Override
public void setDefaults() {
setShowName(Global.getParameter("options.locus.shownames", false));
setShowValue(Global.getParameter("options.locus.showvalues", false));
// setColor(Global.getParameter("options.locus.color", 0), Global
// .getParameter("options.locus.pcolor", (Color) null));
setColor(Global.getParameter("options.locus.color", 0));
setColorType(Global.getParameter("options.locus.colortype", 0));
setFilled(Global.getParameter("options.locus.filled", false));
setHidden(Cn.Hidden);
setObtuse(Cn.Obtuse);
setSolid(Cn.Solid);
setLarge(Cn.LargeFont);
setBold(Cn.BoldFont);
}
@Override
public void setTargetDefaults() {
setShowName(Global.getParameter("options.locus.shownames", false));
setShowValue(Global.getParameter("options.locus.showvalues", false));
setColor(Global.getParameter("options.locus.color", 0), Global.getParameter("options.locus.pcolor", (ExpressionColor) null, this));
setColorType(Global.getParameter("options.locus.colortype", 0));
setFilled(Global.getParameter("options.locus.filled", false));
setHidden(Cn.Hidden);
setObtuse(Cn.Obtuse);
setSolid(Cn.Solid);
setLarge(Cn.LargeFont);
setBold(Cn.BoldFont);
}
/**
* In order to see if the function must be recomputed or not, we have to
* register, in the DriverObjectList, all objects the function depends on.
* There are many possibilities for an object to be in this list : if it's
* used in the def of the function, if it's in the min or max text box,
* etc...
*
* @param c
*/
@Override
public void searchDependencies(final Construction c) {
DriverObjectList.clear();
if (RekValidating) {
return;
} // should not happen!
RekValidating = true;
Enumeration e = c.elements();
while (e.hasMoreElements()) {
((ConstructionObject) e.nextElement()).setRekFlag(false);
}
final ConstructionObject oEX[] = EX.getDepList().getArray();
final ConstructionObject oEY[] = EY.getDepList().getArray();
final ConstructionObject oVarMin[] = VarMin.getDepList().getArray();
final ConstructionObject oVarMax[] = VarMax.getDepList().getArray();
final ConstructionObject oDVar[] = DVar.getDepList().getArray();
for (final ConstructionObject element : oEX) {
recursiveSearchDependencies((ConstructionObject) element);
}
for (final ConstructionObject element : oEY) {
recursiveSearchDependencies((ConstructionObject) element);
}
for (final ConstructionObject element : oVarMin) {
recursiveSearchDependencies((ConstructionObject) element);
}
for (final ConstructionObject element : oVarMax) {
recursiveSearchDependencies((ConstructionObject) element);
}
for (final ConstructionObject element : oDVar) {
recursiveSearchDependencies((ConstructionObject) element);
}
e = c.elements();
while (e.hasMoreElements()) {
final ConstructionObject oc = (ConstructionObject) e.nextElement();
if ((oc.isRekFlag()) && (oc.isDriverObject())) {
DriverObjectList.addElement(oc);
}
}
RekValidating = false;
NeedsRecompute = true;
}
/**
* Recursive method called by the searchDependencies method
*
* @param o
*/
public void recursiveSearchDependencies(final ConstructionObject o) {
if (o.isRekFlag()) {
return;
}
o.setRekFlag(true);
final ConstructionObject d[] = o.getDepArray();
for (final ConstructionObject element : d) {
recursiveSearchDependencies(element);
}
}
/**
* Time consuming method which is called only if it's really necessary :
*/
@Override
public void compute() {
// Empty the vector which contains the point set of the plot :
V.clear();
if (!Valid) {
return;
}
// Initialisation of varmin, varmax and d (the step variable) :
double varmin, varmax, d;
try {
varmin = VarMin.getValue();
varmax = VarMax.getValue();
d = DVar.getValue();
if (varmin > varmax) {
final double h = varmin;
varmin = varmax;
varmax = h;
}
if (d < 0) {
d = -d;
}
} catch (final Exception e) {
Valid = false;
return;
}
// X[0] represents the 't' variable for parametric plots.
// When you give X[0] a t value, EX.getValue() and EY.getValue()
// automatically returns the x(t) and y(t) value.
// If it's a cartesian function, X[0] represents the 'x' value
// then EX.getValue() always returns x and EY.getValue() returns
// the f(x) value.
X[0] = varmin;
// If the function may have discontinuity problems, computing is
// slower, because it checks for each point if a segment will have
// to be displayed or not :
if (mayHaveDiscontinuityPb()) {
if (d == 0) {
try {
// if the user leaves the step text box empty in the
// properties bar, the step d will represents only one pixel
// :
d = new Expression("1/pixel", getConstruction(), this)
.getValue();
} catch (final Exception ex) {
}
// if the step d defined by user is too small, then correct it :
} else if (d < (varmax - varmin) / 1000) {
d = (varmax - varmin) / 1000;
}
double x1 = 0, y1 = 0, x2 = 0, y2 = 0, yM = 0, X0 = 0;
final int nbsteps = (int) Math.round((varmax - varmin) / d) + 1;
try {
x1 = EX.getValue();
y1 = EY.getValue();
V.add(new Coordinates(x1, y1, false));
} catch (final Exception ex) {
}
/*
* A(x1,y1=f(x1)) is the first point. B(x2,y2=f(x2)) is the second
* point. In order to know if a segment must join these two points,
* we compute the point M(xM,yM=f(xM)), where xM=(x1+x2)/2 :
*/
for (int i = 1; i < nbsteps; i++) {
try {
X0 = X[0];
X[0] += d / 2;
EX.getValue();
yM = EY.getValue();
X[0] += d / 2;
x2 = EX.getValue();
y2 = EY.getValue();
// If A,M and B are on the same horizontal line
// then segment must be drawn (true) :
if ((y1 == yM) && (y2 == yM)) {
V.add(new Coordinates(x2, y2, true));
// if f(xM) is in open interval ]f(x1),f(x2)[ :
} else if (((yM > y1) && (yM < y2))
|| ((yM > y2) && (yM < y1))) {
final double mm = Math.abs(y1 - yM) / Math.abs(y2 - yM);
// This is a weird thing, but it seems to work in
// lots of "basic" situations :
// If the distance |yM-y1| (or |yM-y2|)
// represents 10% of the distance |yM-y2| (or |yM-y1|)
// then may be it's a discontinuity problem, so
// don't draw the segment [AB] (false)
if ((mm < 0.1) || (mm > 10)) {
V.add(new Coordinates(x2, y2, false));
// Otherwise draw the segment [AB] :
} else {
V.add(new Coordinates(x2, y2, true));
}
// if f(xM) is not in interval [f(x1),f(x2)], don't
// draw the segment [AB] (false) :
} else {
V.add(new Coordinates(x2, y2, false));
}
x1 = x2;
y1 = y2;
} catch (final Exception ex) {
try {
X[0] = X0 + d;
x1 = EX.getValue();
y1 = EY.getValue();
V.add(new Coordinates(x1, y1, false));
} catch (final Exception ex1) {
}
}
}
} else {
if (d == 0) {
d = (varmax - varmin) / 100;
} else if (d < (varmax - varmin) / 1000) {
d = (varmax - varmin) / 1000;
}
final int nbsteps = (int) Math.round((varmax - varmin) / d) + 1;
for (int i = 0; i < nbsteps; i++) {
try {
V.add(new Coordinates(EX.getValue(), EY.getValue()));
} catch (final Exception ex) {
}
X[0] += d;
}
}
}
public void setNeedsToRecompte(final boolean n) {
NeedsRecompute = n;
}
/**
* Check all objects the function depends on. If one of the have changed,
* this function return true.
*
* @return
*/
@Override
public boolean needsToRecompute() {
boolean needs = false;
final Enumeration pl = DriverObjectList.elements();
while (pl.hasMoreElements()) {
final DriverObject oc = (DriverObject) pl.nextElement();
if (oc.somethingChanged()) {
// There is a "ClearList" which will be cleared
// at the end of the Construction.computeHeavyObjects method :
Global.addClearList(oc);
needs = true;
}
}
// Also needs to compute when user zoom or move figure in the window :
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;
}
@Override
public void setFilled(final boolean flag) {
Filled = flag;
}
@Override
public boolean isFilled() {
return Filled;
}
@Override
public String getTag() {
return "Function";
}
@Override
public int getN() {
return N.next();
}
@Override
public void updateText() {
if (EX != null && EY != null) {
setText(text2(Global.name("text.function"), EX.toString(), EY
.toString()));
} else {
setText(text2(Global.name("text.function"), "", ""));
}
}
@Override
public void validate() {
if (EX != null && EY != null) {
Valid = EX.isValid() && EY.isValid() && VarMin.isValid()
&& VarMax.isValid() && DVar.isValid();
} else {
Valid = false;
}
}
public void setExpressions(final String t, String ex, final String ey) {
final StringTokenizer tok = new StringTokenizer(t);
Var = new String[tok.countTokens()];
X = new double[tok.countTokens()];
int i = 0;
while (tok.hasMoreTokens()) {
Var[i++] = tok.nextToken();
}
if (ex.equals("")) {
ex = Var[0];
}
EX = new Expression(ex, getConstruction(), this, Var);
EY = new Expression(ey, getConstruction(), this, Var);
validate();
searchDependencies(Cn);
}
public boolean isCartesian() {
return EX.toString().equals("x");
}
public boolean mayHaveDiscontinuityPb() {
final String Pbs[] = { "floor(", "ceil(", "tan(", "sign(" };
// Continuity pbs are only checked for cartesian functions :
if (EX.toString().equals("x")) {
for (final String pb : Pbs) {
if ((EY.toString().indexOf(pb) != -1)) {
return true;
}
}
}
return false;
}
public void setRange(final String min, final String max, final String d) {
VarMin = new Expression(min, getConstruction(), this);
VarMax = new Expression(max, getConstruction(), this);
DVar = new Expression(d, getConstruction(), this);
searchDependencies(Cn);
}
@Override
public String getEX() {
if (EX != null) {
return EX.toString();
} else {
return Var[0];
}
}
@Override
public String getEY() {
if (EY != null) {
return EY.toString();
} else {
return "0";
}
}
FunctionPolygonFiller PF = null;
double C1, C2;
int C, R, W, H;
@Override
public void paint(final MyGraphics g, final ZirkelCanvas zc) {
if (!Valid || mustHide(zc)) {
return;
}
Coordinates C = null;
Enumeration e = V.elements();
double c, r;
g.setColor(this);
String s=getDisplayText();
if (!s.equals("")) {
g.setLabelColor(this);
setFont(g);
int n = s.length();
if(s.startsWith("$")){//crise de dollarophobie
s= s.substring(1, n-1);
}
if(V.size()>5){
//coordonnées du point d'attache
final Coordinates attache=(Coordinates) V.elementAt((int) Math.floor(V.size()/2+3));
final Coordinates precedent=(Coordinates) V.elementAt((int) Math.floor(V.size()/2)+2);
double Dx=attache.X-precedent.X;
double Dy=attache.Y-precedent.Y;
double norme=4*Math.sqrt(Dx*Dx+Dy*Dy);
drawLabel(g, "$\\mathcal{C}_{"+s+"}$", zc, attache.X, attache.Y,0,0, Dy/norme, -Dx/norme);
}
}
if (Special) {
if (Filled) {
PF = new FunctionPolygonFiller(g, this, zc.getY(), zc.getY() + zc.getHeight());
while (e.hasMoreElements()) {
C = (Coordinates) e.nextElement();
PF.add(zc.col(C.X), zc.row(C.Y));
}
PF.fillPolygon(zc.row(0));
e = V.elements();
g.setColor(this);
}
while (e.hasMoreElements()) {
C = (Coordinates) e.nextElement();
PointObject.drawPoint(g, zc, this, C.X, C.Y, Type);
}
} else if (Tracked) {
zc.UniversalTrack.TrackIG.setColor(this);
zc.UniversalTrack.setActive(true);
final PolygonDrawer pd = new PolygonDrawer(false,g, this);
final PolygonDrawer pdt = new PolygonDrawer(false,zc.UniversalTrack.TrackIG, this);
if (e.hasMoreElements()) {
C = (Coordinates) e.nextElement();
c0 = zc.col(C.X);
r0 = zc.row(C.Y);
pd.startPolygon(c0, r0);
pdt.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);
pdt.drawTo(c, r);
} else {
pd.finishPolygon();
pdt.finishPolygon();
pd.startPolygon(c, r);
pdt.startPolygon(c, r);
}
}
pd.finishPolygon();
pdt.finishPolygon();
} else {
if (Filled) {
PF = new FunctionPolygonFiller(g, this, zc.getY(), zc.getY() + zc.getHeight());
while (e.hasMoreElements()) {
C = (Coordinates) e.nextElement();
PF.add(zc.col(C.X), zc.row(C.Y));
}
PF.fillPolygon(zc.row(0));
e = V.elements();
g.setColor(this);
}
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 (C.join) {
pd.drawTo(c, r);
} else {
pd.finishPolygon();
pd.startPolygon(c, r);
}
}
pd.finishPolygon();
}
/***********************
* Painting the bounds *
***********************/
double w = 2*zc.pointSize()+2*zc.getOne(), x, y;
if(!VarMin.toString().equals("windowcx-windoww")) {
final Coordinates coord0 = (Coordinates) V.firstElement();
if(isMinClosed) {
x = zc.col(coord0.X) - zc.pointSize() - zc.getOne();
y = zc.row(coord0.Y) - zc.pointSize() - zc.getOne();
g.fillOval(x, y, w, w, this.getColor());
} else if(isMinOpen) {
//final Coordinates coord1 = (Coordinates) V.elementAt(Math.min(0, V.size()));
final Coordinates coord1 = (Coordinates) V.elementAt(V.size()==1?0:1);
double dx = zc.col(coord0.X) - zc.col(coord1.X);
double dy = zc.row(coord0.Y) - zc.row(coord1.Y);
x = w/2*dx/(Math.sqrt(dx*dx+dy*dy)) + zc.col(coord0.X) - zc.pointSize() - zc.getOne();
y = w/2*dy/(Math.sqrt(dx*dx+dy*dy)) + zc.row(coord0.Y) - zc.pointSize() - zc.getOne();
double alpha = Math.atan2(coord0.Y-coord1.Y, coord0.X-coord1.X)*180/Math.PI;
g.drawArc(x, y, w, w, alpha+90, 180);
}
}
if(!VarMax.toString().equals("windowcx+windoww")) {
final Coordinates coord0=(Coordinates) V.lastElement();
if(isMaxClosed) {
w = 2*zc.pointSize()+2*zc.getOne();
x = zc.col(coord0.X)-zc.pointSize()-zc.getOne();
y = zc.row(coord0.Y)-zc.pointSize()-zc.getOne();
g.fillOval(x, y, w, w, this.getColor());
} else if(isMaxOpen) {
final Coordinates coord1 = (Coordinates) V.elementAt(Math.max(V.size()-2,0));
double dx = zc.col(coord0.X) - zc.col(coord1.X);
double dy = zc.row(coord0.Y) - zc.row(coord1.Y);
x = w/2*dx/(Math.sqrt(dx*dx+dy*dy)) + zc.col(coord0.X) - zc.pointSize() - zc.getOne();
y = w/2*dy/(Math.sqrt(dx*dx+dy*dy)) + zc.row(coord0.Y) - zc.pointSize() - zc.getOne();
double alpha = Math.atan2(coord0.Y-coord1.Y, coord0.X-coord1.X)*180/Math.PI;
g.drawArc(x, y, w, w, alpha+90, 180);
}
}
}
@Override
public double getValue() throws ConstructionException {
if (!Valid) {
throw new InvalidException("exception.invalid");
}
return X[0];
}
public double getValue(final String var) throws ConstructionException {
if (!Valid) {
throw new InvalidException("exception.invalid");
}
for (int i = 0; i < Var.length; i++) {
if (var.equals(Var[i])) {
return X[i];
}
}
return X[0];
}
public double getIntegral() throws ConstructionException {
return getSum();
}
@Override
public String getDisplayValue() {
if (getEX().equals(getVar())) {
return EY.toString();
} else {
return "(" + EX.toString() + "," + EY.toString() + ")";
}
}
// Mainly to select the track for delete
@Override
public boolean nearto(final int x, final int y, final ZirkelCanvas zc) {
if (!displays(zc)) {
return false;
}
final Enumeration e = V.elements();
final double xx = zc.x(x), yy = zc.y(y);
final double mymax = (7 / Cn.getPixel());
if (Special) {
Coordinates CS;
while (e.hasMoreElements()) {
CS = (Coordinates) e.nextElement();
if ((Math.abs(CS.X - xx) < mymax)
&& (Math.abs(CS.Y - yy) < mymax)) {
return true;
}
}
} else {
double xA = 0, yA = 0, xB = 0, yB = 0;
Coordinates CS0, CS1;
if (e.hasMoreElements()) {
CS0 = (Coordinates) e.nextElement();
xA = CS0.X;
yA = CS0.Y;
}
while (e.hasMoreElements()) {
CS1 = (Coordinates) e.nextElement();
xB = CS1.X;
yB = CS1.Y;
final double p1 = (xx - xA) * (xB - xA) + (yy - yA) * (yB - yA);
final double p2 = (xx - xB) * (xA - xB) + (yy - yB) * (yA - yB);
if ((p1 > 0) && (p2 > 0)) {
final double aa = xB - xA, bb = yB - yA, cc = bb * xA - aa
* yA;
final double d = Math.abs(-bb * xx + aa * yy + cc)
/ Math.sqrt(aa * aa + bb * bb);
if (d < mymax) {
return true;
}
}
xA = xB;
yA = yB;
}
}
return false;
}
public boolean EditAborted;
@Override
public void printArgs(final XmlWriter xml) {
xml.printArg("x", EX.toString());
xml.printArg("y", EY.toString());
xml.printArg("var", getVar());
xml.printArg("min", "" + VarMin);
xml.printArg("max", "" + VarMax);
xml.printArg("d", "" + DVar);
if (Special) {
xml.printArg("special", "true");
}
printType(xml);
if (Filled) {
xml.printArg("filled", "true");
}
if (getCenter() != null) {
xml.printArg("center", getCenter().getName());
}
xml.printArg("color", ""+getColorIndex());
if(isMinOpen || isMinClosed) {
xml.printArg("minbound", isMinOpen?"0":"1"); //0 = open ; 1 = closed
}
if(isMaxOpen || isMaxClosed) {
xml.printArg("maxbound", isMaxOpen?"0":"1");
}
}
@Override
public void setType(final int type) {
Type = type;
}
@Override
public int getType() {
return Type;
}
public void printType(final XmlWriter xml) {
if (Type != 0) {
switch (Type) {
case DIAMOND:
xml.printArg("shape", "diamond");
break;
case CIRCLE:
xml.printArg("shape", "circle");
break;
case DOT:
xml.printArg("shape", "dot");
break;
case CROSS:
xml.printArg("shape", "cross");
break;
case DCROSS:
xml.printArg("shape", "dcross");
break;
}
}
}
@Override
public ConstructionObject copy(final double x, final double y) {
final FunctionObject fo = new FunctionObject(getConstruction());
fo.copyProperties(this);
fo.setFilled(this.isFilled()); //bug fix concerning filled curves in macro constructions
fo.EX = new Expression(EX.toString(), getConstruction(), fo, Var);
fo.EY = new Expression(EY.toString(), getConstruction(), fo, Var);
fo.VarMin = new Expression(VarMin.toString(), getConstruction(), fo);
fo.VarMax = new Expression(VarMax.toString(), getConstruction(), fo);
fo.DVar = new Expression(DVar.toString(), getConstruction(), fo);
fo.Special = Special;
final ConstructionObject O = getTranslation();
fo.setTranslation(this);
fo.EX.translate();
fo.EY.translate();
fo.VarMin.translate();
fo.VarMax.translate();
fo.DVar.translate();
fo.translateConditionals();
fo.X = X;
fo.Var = Var;
fo.validate();
fo.setTranslation(O);
fo.searchDependencies(Cn.getTranslation());
return fo;
}
@Override
public boolean onlynearto(final int x, final int y, final ZirkelCanvas zc) {
return false;
// return nearto(x,y,zc);
}
@Override
public boolean equals(final ConstructionObject o) {
return false;
}
@Override
public Enumeration depending() {
DL.reset();
addDepending(EX);
addDepending(EY);
addDepending(VarMin);
addDepending(VarMax);
addDepending(DVar);
return DL.elements();
}
public void addDepending(final Expression E) {
if (E != null) {
final Enumeration e = E.getDepList().elements();
while (e.hasMoreElements()) {
DL.add((ConstructionObject) e.nextElement());
}
}
}
@Override
public boolean hasUnit() {
return false;
}
public double evaluateF(final double x[]) throws ConstructionException {
int n = x.length;
if (n > X.length) {
n = X.length;
}
for (int i = 0; i < n; i++) {
X[i] = x[i];
}
for (int i = n; i < X.length; i++) {
X[i] = 0;
}
try {
return EY.getValue();
} catch (final Exception e) {
throw new ConstructionException("");
}
}
@Override
public double evaluateF(final double x) throws ConstructionException {
X[0] = x;
for (int i = 1; i < X.length; i++) {
X[i] = 0;
}
try {
return EY.getValue();
} catch (final Exception e) {
throw new ConstructionException("");
}
}
@Override
public int getDistance(final PointObject P) {
double varmin, varmax, dvar;
try {
varmin = VarMin.getValue();
varmax = VarMax.getValue();
dvar = DVar.getValue();
if (varmin > varmax) {
final double h = varmin;
varmin = varmax;
varmax = h;
}
if (dvar < 0) {
dvar = -dvar;
}
if (dvar == 0) {
dvar = (varmax - varmin) / 100;
} else if (dvar < (varmax - varmin) / 1000) {
dvar = (varmax - varmin) / 1000;
}
} catch (final Exception e) {
Valid = false;
return Integer.MAX_VALUE;
}
try {
// if it's a cartesian function, try to calculate the "real" coords
// :
if ((getEX().equals("x"))) {
double x = (P.getX() < varmin) ? varmin : P.getX();
x = (P.getX() > varmax) ? varmax : x;
final double y = evaluateF(x);
final double dd = Math.sqrt((P.getX() - x) * (P.getX() - x)
+ (P.getY() - y) * (P.getY() - y));
return (int) Math.round(dd * Cn.getPixel());
}
} catch (final Exception e) {
}
try {
// if it's a parmetric curve and function is just plot with points :
if ((!getEX().equals("x")) && (Special)) {
if (P.haveBoundOrder()) {
X[0] = P.getBoundOrder();
final double dd = Math.sqrt((P.getX() - EX.getValue())
* (P.getX() - EX.getValue())
+ (P.getY() - EY.getValue())
* (P.getY() - EY.getValue()));
return (int) Math.round(dd * Cn.getPixel());
} else {
Coordinates CS;
final Enumeration e = V.elements();
double delta0 = 0, delta1 = 0, xx = 0, yy = 0;
int i = 0;
if (e.hasMoreElements()) {
CS = (Coordinates) e.nextElement();
delta0 = Math.abs(CS.X - P.getX())
+ Math.abs(CS.Y - P.getY());
xx = CS.X;
yy = CS.Y;
}
while (e.hasMoreElements()) {
i++;
CS = (Coordinates) e.nextElement();
delta1 = Math.abs(CS.X - P.getX())
+ Math.abs(CS.Y - P.getY());
if (delta1 < delta0) {
delta0 = delta1;
xx = CS.X;
yy = CS.Y;
}
}
final double dd = Math.sqrt((P.getX() - xx)
* (P.getX() - xx) + (P.getY() - yy)
* (P.getY() - yy));
return (int) Math.round(dd * Cn.getPixel());
}
}
} catch (final Exception e) {
Valid = false;
return Integer.MAX_VALUE;
}
// Otherwise, at Least get the approx coordinates on the polygon :
if (needsToRecompute()) {
compute();
}
final Enumeration e = V.elements();
double x = 0, y = 0, x0 = 0, y0 = 0, dmin = 0;
boolean started = false;
while (e.hasMoreElements()) {
final Coordinates c = (Coordinates) e.nextElement();
final double x1 = c.X;
final double y1 = c.Y;
if (!started) {
dmin = Math.sqrt((P.getX() - x1) * (P.getX() - x1)
+ (P.getY() - y1) * (P.getY() - y1));
x0 = x = x1;
y0 = y = y1;
started = true;
} else {
if (c.flag) {
double h = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
if (h < 1e-10) {
h = 1e-10;
}
double g = (P.getX() - x0) * (x1 - x0) + (P.getY() - y0)
* (y1 - y0);
if (g < 0) {
g = 0;
}
if (g > h) {
g = h;
}
final double x2 = x0 + g / h * (x1 - x0), y2 = y0 + g / h
* (y1 - y0);
final double d = Math.sqrt((P.getX() - x2)
* (P.getX() - x2) + (P.getY() - y2)
* (P.getY() - y2));
if (d < dmin) {
dmin = d;
x = x2;
y = y2;
}
}
x0 = x1;
y0 = y1;
}
}
if (started) {
P.Valid = true;
final double dd = Math.sqrt((P.getX() - x) * (P.getX() - x)
+ (P.getY() - y) * (P.getY() - y));
return (int) Math.round(dd * Cn.getPixel());
} else {
P.Valid = false;
}
return Integer.MAX_VALUE;
}
@Override
public void project(final PointObject P) {
double varmin, varmax, dvar;
try {
varmin = VarMin.getValue();
varmax = VarMax.getValue();
dvar = DVar.getValue();
if (varmin > varmax) {
final double h = varmin;
varmin = varmax;
varmax = h;
}
if (dvar < 0) {
dvar = -dvar;
}
if (dvar == 0) {
dvar = (varmax - varmin) / 100;
} else if (dvar < (varmax - varmin) / 1000) {
dvar = (varmax - varmin) / 1000;
}
} catch (final Exception e) {
Valid = false;
return;
}
try {
// if P is a PointOn (a parmetric curve) and function is just plot
// with points :
if ((!getEX().equals("x")) && (P.isPointOnOrMagnet()) && (Special)) {
if (P.haveBoundOrder()) {
X[0] = P.getBoundOrder();
P.setXY(EX.getValue(), EY.getValue());
return;
} else {
Coordinates CS;
final Enumeration e = V.elements();
double delta0 = 0, delta1 = 0, xx = 0, yy = 0;
int i = 0, k = 0;
if (e.hasMoreElements()) {
CS = (Coordinates) e.nextElement();
delta0 = Math.abs(CS.X - P.getX())
+ Math.abs(CS.Y - P.getY());
xx = CS.X;
yy = CS.Y;
}
while (e.hasMoreElements()) {
i++;
CS = (Coordinates) e.nextElement();
delta1 = Math.abs(CS.X - P.getX())
+ Math.abs(CS.Y - P.getY());
if (delta1 < delta0) {
k = i;
delta0 = delta1;
xx = CS.X;
yy = CS.Y;
}
}
P.setXY(xx, yy);
P.setBoundOrder(varmin + k * dvar);
return;
}
}
} catch (final Exception e) {
Valid = false;
return;
}
try {
// if P is a PointOn (a cartesian function), try to calculate the
// "real" coords :
if ((P.isPointOnOrMagnet()) && (getEX().equals("x"))) {
double x = (P.getX() < varmin) ? varmin : P.getX();
x = (P.getX() > varmax) ? varmax : x;
final double y = evaluateF(x);
P.move(x, y);
return;
}
} catch (final Exception e) {
}
// Otherwise, at Least get the approx coordinates on the polygon :
if (needsToRecompute()) {
compute();
}
final Enumeration e = V.elements();
double x = 0, y = 0, x0 = 0, y0 = 0, dmin = 0;
boolean started = false;
while (e.hasMoreElements()) {
final Coordinates c = (Coordinates) e.nextElement();
final double x1 = c.X;
final double y1 = c.Y;
if (!started) {
dmin = Math.sqrt((P.getX() - x1) * (P.getX() - x1)
+ (P.getY() - y1) * (P.getY() - y1));
x0 = x = x1;
y0 = y = y1;
started = true;
} else {
if (c.flag) {
double h = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
if (h < 1e-10) {
h = 1e-10;
}
double g = (P.getX() - x0) * (x1 - x0) + (P.getY() - y0)
* (y1 - y0);
if (g < 0) {
g = 0;
}
if (g > h) {
g = h;
}
final double x2 = x0 + g / h * (x1 - x0), y2 = y0 + g / h
* (y1 - y0);
final double d = Math.sqrt((P.getX() - x2)
* (P.getX() - x2) + (P.getY() - y2)
* (P.getY() - y2));
if (d < dmin) {
dmin = d;
x = x2;
y = y2;
}
}
x0 = x1;
y0 = y1;
}
}
if (started) {
P.setXY(x, y);
P.Valid = true;
} else {
P.Valid = false;
}
}
public double getSum() {
double varmin, varmax, dvar;
boolean reverse = false;
final boolean parametric = !getEX().equals(getVar());
try {
varmin = VarMin.getValue();
varmax = VarMax.getValue();
dvar = DVar.getValue();
if (varmin > varmax) {
final double h = varmin;
varmin = varmax;
varmax = h;
reverse = true;
}
if (dvar < 0) {
dvar = -dvar;
}
if (dvar == 0) {
dvar = (varmax - varmin) / 100;
} else if (dvar < (varmax - varmin) / 1000) {
dvar = (varmax - varmin) / 1000;
}
} catch (final Exception e) {
Valid = false;
return 0;
}
X[0] = varmin;
double x0 = 0, y0 = 0;
boolean started = false;
double sum = 0;
while (true) {
try {
final double x1 = EX.getValue();
final double y1 = EY.getValue();
if (parametric) {
double x = 0, y = 0;
if (getCenter() != null) {
x = getCenter().getX();
y = getCenter().getY();
}
if (started) {
sum += ((x0 - x) * (y1 - y) - (y0 - y) * (x1 - x)) / 2;
}
} else {
if (started) {
if (Special) {
if (reverse) {
sum += (x1 - x0) * y1;
} else {
sum += (x1 - x0) * y0;
}
} else {
sum += (x1 - x0) * (y0 + y1) / 2;
}
}
}
x0 = x1;
y0 = y1;
started = true;
} catch (final Exception e) {
}
if (X[0] >= varmax) {
break;
}
X[0] = X[0] + dvar;
if (X[0] > varmax) {
X[0] = varmax;
}
}
return sum;
}
public double getLength() {
double varmin, varmax, dvar;
try {
varmin = VarMin.getValue();
varmax = VarMax.getValue();
dvar = DVar.getValue();
if (varmin > varmax) {
final double h = varmin;
varmin = varmax;
varmax = h;
}
if (dvar < 0) {
dvar = -dvar;
}
if (dvar == 0) {
dvar = (varmax - varmin) / 100;
} else if (dvar < (varmax - varmin) / 1000) {
dvar = (varmax - varmin) / 1000;
}
} catch (final Exception e) {
Valid = false;
return 0;
}
X[0] = varmin;
double x0 = 0, y0 = 0;
boolean started = false;
double sum = 0;
while (true) {
try {
final double x1 = EX.getValue();
final double y1 = EY.getValue();
if (started) {
sum += Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0)
* (y1 - y0));
}
started = true;
x0 = x1;
y0 = y1;
} catch (final Exception e) {
}
if (X[0] >= varmax) {
break;
}
X[0] = X[0] + dvar;
if (X[0] > varmax) {
X[0] = varmax;
}
}
return sum;
}
@Override
public boolean isSpecial() {
return Special;
}
@Override
public void setSpecial(final boolean f) {
Special = f;
}
@Override
public void project(final PointObject P, final double alpha) {
project(P);
}
@Override
public boolean maybeTransparent() {
return true;
}
@Override
public boolean canDisplayName() {
return true;
}
@Override
public boolean showValue(){
return false;
}
public void setCenter(final String s) {
if (Cn == null) {
return;
}
Center = new Expression("@\"" + s + "\"", Cn, this);
}
@Override
public boolean isFilledForSelect() {
return false;
}
public PointObject getCenter() {
try {
return (PointObject) Center.getObject();
} catch (final Exception e) {
return null;
}
}
public String getVar() {
String vars = Var[0];
for (int i = 1; i < Var.length; i++) {
vars = vars + " " + Var[i];
}
return vars;
}
@Override
public boolean canInteresectWith(final ConstructionObject o) {
return true;
}
@Override
public boolean isDriverObject() {
return true;
}
@Override
public boolean somethingChanged() {
boolean b = !EX.toString().equals(LASTEX);
b = b || !EY.toString().equals(LASTEY);
return b;
}
@Override
public void clearChanges() {
LASTEX = EX.toString();
LASTEY = EY.toString();
}
@Override
public void repulse(final PointObject P) {
project(P);
}
/******************************
* Closed or Open Intervals ***
*******************************/
public void setMinClosed(boolean b) {
isMinClosed = b;
}
public void setMinOpen(boolean b) {
isMinOpen = b;
}
public boolean getMinClosed() {
return isMinClosed;
}
public boolean getMinOpen() {
return isMinOpen;
}
public void setMaxClosed(boolean b) {
isMaxClosed = b;
}
public void setMaxOpen(boolean b) {
isMaxOpen = b;
}
public boolean getMaxClosed() {
return isMaxClosed;
}
public boolean getMaxOpen() {
return isMaxOpen;
}
}