/*
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.cas;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoFunctionFreehand;
import org.geogebra.common.kernel.algos.AlgoPointOnPath;
import org.geogebra.common.kernel.algos.TangentAlgo;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.commands.EvalInfo;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoLine;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.statistics.AlgoFitPoly;
/**
* Algorithm for tangent of function
*/
public class AlgoTangentFunctionPoint extends AlgoElement
implements TangentAlgo {
private GeoPointND P; // input
private GeoLine tangent; // output
private GeoFunction f;
private GeoPoint T;
private boolean pointOnFunction;
private GeoFunction deriv;
private AlgoDerivative algo;
private boolean freehand;
private AlgoFunctionFreehand freehandAlgo;
private GeoList freehandList;
private AlgoFitPoly algoFitPoly;
private GeoPoint[] points;
private GeoList geoList;
/**
* @param cons
* construction
* @param label
* label for output
* @param P
* point on function
* @param f
* function
*/
public AlgoTangentFunctionPoint(Construction cons, String label,
GeoPointND P, GeoFunction f) {
this(cons, P, f);
tangent.setLabel(label);
}
/**
* @param cons
* construction
* @param P
* point on function
* @param f
* function
*/
public AlgoTangentFunctionPoint(Construction cons, GeoPointND P,
GeoFunction f) {
super(cons);
this.P = P;
this.f = f;
tangent = new GeoLine(cons);
// check if P is defined as a point of the function's graph
pointOnFunction = false;
if (P.getParentAlgorithm() instanceof AlgoPointOnPath) {
AlgoPointOnPath algoPoint = (AlgoPointOnPath) P
.getParentAlgorithm();
pointOnFunction = algoPoint.getPath() == f;
}
if (pointOnFunction) {
T = (GeoPoint) P;
} else {
T = new GeoPoint(cons);
}
tangent.setStartPoint(T);
if (f.getParentAlgorithm() instanceof AlgoFunctionFreehand) {
// APPROXIMATE tangent for Freehand Functions
freehand = true;
freehandAlgo = (AlgoFunctionFreehand) f.getParentAlgorithm();
freehandList = freehandAlgo.getList();
geoList = new GeoList(cons);
// # of points to sample (must be even)
int steps = 10;
points = new GeoPoint[steps];
// order 5, 10 steps, 100 slices seems to work nicely
// mockup http://tube.geogebra.org/student/m683647
algoFitPoly = new AlgoFitPoly(cons, geoList,
new GeoNumeric(cons, 5));
cons.removeFromConstructionList(algoFitPoly);
} else {
// derivative of f
// use fast non-CAS derivative
algo = new AlgoDerivative(cons, f, true, new EvalInfo(false));
deriv = (GeoFunction) algo.getResult();
cons.removeFromConstructionList(algo);
}
setInputOutput(); // for AlgoElement
compute();
}
@Override
public Commands getClassName() {
return Commands.Tangent;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_TANGENTS;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[2];
input[0] = (GeoElement) P;
input[1] = f;
setOutputLength(1);
setOutput(0, tangent);
setDependencies(); // done by AlgoElement
}
/**
* @return resulting tangent
*/
public GeoLine getTangent() {
return tangent;
}
/**
* @return function
*/
GeoFunction getFunction() {
return f;
}
/**
* @return point on function
*/
GeoPointND getPoint() {
return P;
}
/**
* @return point on function
*/
GeoPoint getTangentPoint() {
return T;
}
@Override
public GeoPoint getTangentPoint(GeoElement geo, GeoLine line) {
if (geo != f) {
return null;
}
if (line != tangent) {
return null;
}
return T;
}
// calc tangent at x=a
@Override
public final void compute() {
if (!(f.isDefined() && P.isDefined()
&& (freehand || deriv.isDefined()))) {
tangent.setUndefined();
return;
}
double slope;
double a = P.getInhomX();
double fa = f.value(a);
if (freehand) {
int steps = points.length;
double min = freehandList.get(0).evaluateDouble();
double max = freehandList.get(1).evaluateDouble();
double step = (max - min) / 100;
double offset = 0;
// adjust if the sample goes outside the domain of the function
if (a + step * (0 - steps / 2) < min) {
offset = min - (a + step * (0 - steps / 2));
} else if (a + step * (points.length - 1 - steps / 2) > max) {
offset = max - (a + step * (points.length - 1 - steps / 2));
}
geoList.clear();
for (int i = 0; i < points.length; i++) {
points[i] = newPoint(a + step * (i - steps / 2) + offset);
geoList.add(points[i]);
}
algoFitPoly.compute();
GeoFunction fun = (GeoFunction) algoFitPoly.getOutput(0);
FunctionVariable fv = fun.getFunction().getFunctionVariable();
ExpressionValue derivFit = fun.getFunction().derivative(fv, kernel);
fv.set(a);
slope = derivFit.evaluateDouble();
} else {
// calc the tangent;
slope = deriv.value(a);
}
tangent.setCoords(-slope, 1.0, a * slope - fa);
if (!pointOnFunction) {
T.setCoords(a, fa, 1.0);
}
}
private GeoPoint newPoint(double a) {
return new GeoPoint(cons, a, f.value(a), 1);
}
@Override
public final String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-30
// simplified to allow better Chinese translation
return getLoc().getPlain("TangentToAatB", f.getLabel(tpl),
"x = x(" + P.getLabel(tpl) + ")");
}
}