package org.geogebra.common.kernel.implicit;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.arithmetic.Equation;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.commands.Commands;
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.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
/**
* Algorithm for computation of tangent curve
*
*/
public class AlgoImplicitPolyTangentLine extends AlgoElement implements
AlgoTangentHelper {
private GeoImplicit poly;
private GeoLineND line;
private GeoImplicit tangentPoly;
/**
* @param c
* construction
* @param poly
* polynomial
* @param line
* parallel line
*/
public AlgoImplicitPolyTangentLine(Construction c, GeoImplicit poly,
GeoLineND line) {
super(c, false);
this.poly = poly;
this.line = line;
tangentPoly = (GeoImplicit) poly.copy();
// tangentPoly.preventPathCreation();
setInputOutput();
compute();
}
@Override
public void compute() {
/*
* calculate tangent curve: dF/dx * x_p + dF/dy * y_p + u_{n-1} +
* 2*u_{n-2} + ... + n*u_0 where u_i are the terms of poly with total
* degree of i.
*/
if (line.isGeoElement3D()) {
return;
}
GeoLine line2D = (GeoLine) this.line;
tangentPoly.setDefined();
if (poly instanceof GeoImplicitCurve
&& poly.getCoeff() == null) {
GeoImplicitCurve inputCurve = ((GeoImplicitCurve) poly);
// build expression Fx*(x-x0)+Fy*(y-y0)
ExpressionNode y1 = new ExpressionNode(kernel, -line2D.getX());
ExpressionNode x1 = new ExpressionNode(kernel, line2D.getY());
x1 = x1.multiply(inputCurve.getDerivativeX().getExpression());
y1 = y1.multiply(inputCurve.getDerivativeY().getExpression());
tangentPoly.fromEquation(new Equation(kernel, x1.plus(y1),
new MyDouble(kernel, 0)), null);
return;
}
double x = line2D.getY();
double y = -line2D.getX();
double[][] coeff = poly.getCoeff();
double[][] newCoeff = new double[coeff.length][];
for (int i = 0; i < coeff.length; i++) {
newCoeff[i] = new double[coeff[i].length];
for (int j = 0; j < coeff[i].length; j++) {
newCoeff[i][j] = 0;
if (i + 1 < coeff.length && j < coeff[i + 1].length) {
newCoeff[i][j] += x * (i + 1) * coeff[i + 1][j];
}
if (j + 1 < coeff[i].length) {
newCoeff[i][j] += y * (j + 1) * coeff[i][j + 1];
}
// helper = helper.plus(vx.wrap().power(i)
// .multiply(vy.wrap().power(j)).multiply(newCoeff[i][j]));
}
}
tangentPoly.setCoeff(PolynomialUtils.coeffMinDeg(newCoeff));
tangentPoly.setDefined();
}
@Override
protected void setInputOutput() {
input = new GeoElement[] { poly.toGeoElement(), line.toGeoElement() };
setOutputLength(1);
setOutput(0, tangentPoly.toGeoElement());
setDependencies();
}
@Override
public Commands getClassName() {
return Commands.Tangent;
}
/**
* @return resulting tangent curve
*/
@Override
public GeoImplicit getTangentCurve() {
return tangentPoly;
}
@Override
public GeoElement getVec() {
return line.toGeoElement();
}
@Override
public boolean vecDefined() {
return line.isDefined();
}
@Override
public void getTangents(GeoPoint[] ip, OutputHandler<GeoLine> tangents) {
int n = 0;
GeoLine line2d = (GeoLine) line;
for (int i = 0; i < ip.length; i++) {
// normal vector does not exist, therefore tangent is not defined
// We need to check if F1 :=dF/dx and F2 :=dF/dy are both zero when
// eval at ip[i]
// The error of F1 is dF1/dx * err(x) + dF1/dy * err(y), where
// err(x) and err(y) satisfies
// | (dF/dx) err(x) + (dF/dy) err(y) | < EPSILON
// So |dF/dx|<= |dF1/dx * err(x) + dF1/dy * err(y)| <= Max(dF1/dx /
// dF/dx, dF1/dy / dF/dy) * EPSILON
// A convenient necessary condition of this is (dF/dx)^2 <= |dF1/dx|
// * EPSILON.
// Not very reasonably, now we use (dF/dx)^2 <= EPSILON only, to
// avoid evaluation of dF1/dx
// TODO: have a more reasonable choice; also we use standard
// precision rather than working precision (might not be a problem)
if (Kernel.isEqual(0,
this.poly.derivativeX(ip[i].inhomX, ip[i].inhomY),
Kernel.STANDARD_PRECISION_SQRT)
&& Kernel.isEqual(0,
this.poly.derivativeY(ip[i].inhomX, ip[i].inhomY),
Kernel.STANDARD_PRECISION_SQRT)) {
continue;
}
tangents.adjustOutputSize(n + 1);
tangents.getElement(n).setCoords(
line2d.getX(),
line2d.getY(),
-ip[i].getX() * line2d.getX() - line2d.getY()
* ip[i].getY());
ip[i].addIncidence(tangents.getElement(n), false);
n++;
}
}
@Override
public GeoPointND getTangentPoint(GeoElement geo, GeoLine l) {
// TODO Auto-generated method stub
return null;
}
}