/*
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.algos.AlgoDependentPoint;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoPointOnPath;
import org.geogebra.common.kernel.algos.TangentAlgo;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.commands.EvalInfo;
import org.geogebra.common.kernel.geos.GeoCurveCartesian;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoLine;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.plugin.Operation;
/**
* @author Victor Franco Espino
* @version 11-02-2007
*
* tangent to Curve f in point P: (b'(t), -a'(t),
* a'(t)*b(t)-a(t)*b'(t))
*/
public class AlgoTangentCurve extends AlgoElement implements TangentAlgo {
private GeoPointND P; // input
private GeoCurveCartesian f, df; // input f
private GeoLine tangent; // output
private GeoPoint T;
private boolean pointOnCurve;
private boolean pointOnCurveSpecial;
private ExpressionValue pointOnCurveSpecialParam;
private AlgoDerivative algo;
/**
* @param cons
* construction
* @param label
* label for output
* @param P
* point on function
* @param f
* curve
*/
public AlgoTangentCurve(Construction cons, String label, GeoPointND P,
GeoCurveCartesian f) {
super(cons);
tangent = new GeoLine(cons);
this.P = P;
initialize(f);
setInputOutput(); // for AlgoElement
compute();
tangent.setLabel(label);
}
/**
* @param f1
* cartesian curve (input)
*/
public void initialize(GeoCurveCartesian f1) {
this.f = f1;
// check if P is defined as a point of the curve's graph
pointOnCurve = false;
if (P.getParentAlgorithm() instanceof AlgoPointOnPath) {
AlgoPointOnPath algoPOP = (AlgoPointOnPath) P.getParentAlgorithm();
pointOnCurve = algoPOP.getPath() == f;
} else if (P.getParentAlgorithm() instanceof AlgoDependentPoint) {
// special code for curve(t)
AlgoDependentPoint algoDP = (AlgoDependentPoint) P
.getParentAlgorithm();
ExpressionNode en = algoDP.getExpression();
if (en.getOperation() == Operation.VEC_FUNCTION
&& en.getLeft().unwrap() == f) {
pointOnCurveSpecial = true;
pointOnCurveSpecialParam = en.getRight().unwrap();
}
}
if (pointOnCurve || pointOnCurveSpecial) {
T = (GeoPoint) P;
} else {
T = new GeoPoint(cons);
}
tangent.setStartPoint(T);
// First derivative of curve f
algo = new AlgoDerivative(cons, f, true, new EvalInfo(false));
this.df = (GeoCurveCartesian) algo.getResult();
cons.removeFromConstructionList(algo);
}
@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 input curve
*/
GeoCurveCartesian getCurve() {
return f;
}
/**
* @return input point on curve
*/
GeoPointND getPoint() {
return P;
}
/**
* @return tangent point
*/
GeoPoint getTangentPoint() {
return T;
}
private double feval[] = new double[2];
private double dfeval[] = new double[2];
@Override
public final void compute() {
if (!(f.isDefined() && P.isDefined())) {
tangent.setUndefined();
return;
}
// first derivative
if (df == null || !df.isDefined()) {
tangent.setUndefined();
return;
}
// calc the tangent;
double tvalue;
if (pointOnCurve) {
tvalue = P.getPathParameter().t;
} else if (pointOnCurveSpecialParam != null) {
tvalue = pointOnCurveSpecialParam.evaluateDouble();
} else {
tvalue = f.getClosestParameter(P, f.getMinParameter());
}
f.evaluateCurve(tvalue, feval);
df.evaluateCurve(tvalue, dfeval);
tangent.setCoords(-dfeval[1], dfeval[0],
feval[0] * dfeval[1] - dfeval[0] * feval[1]);
if (!pointOnCurve && !pointOnCurveSpecial) {
T.setCoords(feval[0], feval[1], 1.0);
}
}
@Override
public GeoPoint getTangentPoint(GeoElement geo, GeoLine line) {
if (geo == f && line == tangent) {
return getTangentPoint();
}
return null;
}
}