/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is 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.
*/
package org.geogebra.common.kernel.algos;
import java.util.TreeSet;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoPoint;
/**
* Finds intersection points of two polynomials (using the roots of their
* difference)
*
* @author Markus Hohenwarter
*/
public class AlgoIntersectFunctionsNewton extends AlgoRootNewton {
private GeoFunction f, g; // input
private GeoPoint startPoint, rootPoint;
private Function diffFunction;
public AlgoIntersectFunctionsNewton(Construction cons, GeoFunction f,
GeoFunction g, GeoPoint startPoint) {
super(cons);
this.f = f;
this.g = g;
this.startPoint = startPoint;
diffFunction = new Function(kernel);
// output
rootPoint = new GeoPoint(cons);
setInputOutput(); // for AlgoElement
compute();
}
public AlgoIntersectFunctionsNewton(Construction cons, String label,
GeoFunction f, GeoFunction g, GeoPoint startPoint) {
this(cons, f, g, startPoint);
rootPoint.setLabel(label);
}
@Override
public Commands getClassName() {
return Commands.Intersect;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_INTERSECT;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[3];
input[0] = f;
input[1] = g;
input[2] = startPoint;
super.setOutputLength(1);
super.setOutput(0, rootPoint);
setDependencies();
}
@Override
public final void compute() {
if (g.isBooleanFunction()) {
if (f.isBooleanFunction()) {
rootPoint.setUndefined();
} else {
computeRootBoolean(g, f);
}
return;
} else if (f.isBooleanFunction()) {
computeRootBoolean(f, g);
return;
}
if (!(f.isDefined() && g.isDefined() && startPoint.isDefined())) {
rootPoint.setUndefined();
} else {
// get difference f - g
Function.difference(f.getFunction(startPoint.inhomX),
g.getFunction(startPoint.inhomX), diffFunction);
double x = calcRoot(diffFunction, startPoint.inhomX);
// check if x and g(x) are defined
if (Double.isNaN(x) || Double.isNaN(g.value(x))) {
rootPoint.setUndefined();
return;
}
double y = f.value(x);
rootPoint.setCoords(x, y, 1.0);
// if we got here we have a new valid rootPoint
// in order to make dynamic moving of the intersecting objects
// a little bit more stable, we try to be clever here:
// let's take the new rootPoints position as the next starting point
// for Newton's method.
// Note: we should only do this if the starting point is not
// labeled,
// i.e. not visible on screen (and was probably created by clicking
// on an intersection)
if (!startPoint.isLabelSet() && startPoint.isIndependent()
&& rootPoint.isDefined()) {
startPoint.setCoords(rootPoint);
}
}
}
private void computeRootBoolean(GeoFunction bool, GeoFunction real) {
if (bool.getFunction().getIneqs() == null) {
bool.getFunction().initIneqs(bool.getFunctionExpression(), bool);
} else if (!bool.isLabelSet()) {
bool.getFunction().updateIneqs();
}
TreeSet<Double> zeros = new TreeSet<Double>();
bool.getFunction().getIneqs().getZeros(zeros);
this.rootPoint.setUndefined();
if (zeros.isEmpty()) {
return;
}
double lower = Double.NaN;
double higher = Double.NaN;
for (Double d : zeros) {
if (d < startPoint.getInhomX()) {
lower = d;
}
if (d >= startPoint.getInhomX()) {
higher = d;
break;
}
}
double x = Double.isNaN(higher) || (startPoint.getInhomX()
- lower < higher - startPoint.getInhomX()) ? lower : higher;
rootPoint.setCoords(x, real.value(x), 1);
}
public GeoPoint getIntersectionPoint() {
return rootPoint;
}
@Override
final public String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-31
// simplified to allow better translation
return getLoc().getPlain("IntersectionPointOfABWithInitialValueC",
input[0].getLabel(tpl), input[1].getLabel(tpl),
startPoint.getLabel(tpl));
}
}