/*
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 org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.geos.GeoConic;
import org.geogebra.common.kernel.geos.GeoLine;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoVec3D;
import org.geogebra.common.kernel.kernelND.AlgoIntersectND;
import org.geogebra.common.kernel.kernelND.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.prover.NoSymbolicParametersException;
import org.geogebra.common.kernel.prover.polynomial.PPolynomial;
import org.geogebra.common.kernel.prover.polynomial.PVariable;
/**
* Two tangents through point P to conic section c
*/
public class AlgoTangentPoint extends AlgoTangentPointND
implements SymbolicParametersBotanaAlgo {
private PPolynomial[] botanaPolynomials;
private PVariable[] botanaVars;
public AlgoTangentPoint(Construction cons, String[] labels, GeoPointND P,
GeoConicND c) {
super(cons, labels, P, c);
}
@Override
protected boolean isIntersectionPointIncident() {
// Too low precision causes tangent not touching the conic GGB-1018
return c.isIntersectionPointIncident((GeoPoint) P,
Kernel.STANDARD_PRECISION) || P.getIncidenceList().contains(c);
}
@Override
protected void setPolar() {
// the tangents are computed by intersecting the
// polar line of P with c
polar = new GeoLine(cons);
c.polarLine((GeoPoint) P, polar);
algoIntersect = new AlgoIntersectLineConic(cons, polar, (GeoConic) c);
// this is only an internal Algorithm that shouldn't be in the
// construction list
cons.removeFromConstructionList(algoIntersect);
tangentPoints = algoIntersect.getIntersectionPoints();
}
@Override
protected void setTangentFromPolar(int i) {
((GeoLine) tangents[i]).setCoords(polar);
}
@Override
protected void setTangents() {
tangents = new GeoLine[2];
tangents[0] = new GeoLine(cons);
tangents[1] = new GeoLine(cons);
((GeoLine) tangents[0]).setStartPoint((GeoPoint) P);
((GeoLine) tangents[1]).setStartPoint((GeoPoint) P);
}
// Made public for LocusEqu
public GeoPoint getPoint() {
return (GeoPoint) P;
}
// Made public for LocusEqu
public GeoConic getConic() {
return (GeoConic) c;
}
/**
* Inits the helping interesection algorithm to take the current position of
* the lines into account. This is important so the the tangent lines are
* not switched after loading a file
*/
@Override
public void initForNearToRelationship() {
// if first tangent point is not on first tangent,
// we switch the intersection points
initForNearToRelationship(tangentPoints, tangents[0], algoIntersect);
}
/**
* Inits the helping interesection algorithm to take the current position of
* the lines into account. This is important so the the tangent lines are
* not switched after loading a file
*
* @param tangentPoints
* tangent points
* @param tangent
* tangent line
* @param algoIntersect
* algo used
*/
static public void initForNearToRelationship(GeoPointND[] tangentPoints,
GeoLineND tangent, AlgoIntersectND algoIntersect) {
// if first tangent point is not on first tangent,
// we switch the intersection points
GeoPoint firstTangentPoint = (GeoPoint) tangentPoints[0];
if (!((GeoLine) tangent).isOnFullLine(firstTangentPoint,
Kernel.MIN_PRECISION)) {
algoIntersect.initForNearToRelationship();
// remember first point
double px = firstTangentPoint.x;
double py = firstTangentPoint.y;
double pz = firstTangentPoint.z;
// first = second
algoIntersect.setIntersectionPoint(0, tangentPoints[1]);
// second = first
tangentPoints[1].setCoords(px, py, pz);
algoIntersect.setIntersectionPoint(1, tangentPoints[1]);
}
}
@Override
protected void updatePolarLine() {
c.polarLine((GeoPoint) P, polar);
}
@Override
protected void updateTangents() {
// calc tangents through tangentPoints
GeoVec3D.lineThroughPoints((GeoPoint) P, (GeoPoint) tangentPoints[0],
(GeoLine) tangents[0]);
GeoVec3D.lineThroughPoints((GeoPoint) P, (GeoPoint) tangentPoints[1],
(GeoLine) tangents[1]);
}
@Override
public PVariable[] getBotanaVars(GeoElementND geo) {
return botanaVars;
}
@Override
public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
throws NoSymbolicParametersException {
/*
* Don't cache this. The equations may be different if the tangent point
* is on the curve.
*/
// source for tangent point of conics:
// www.cs.usfca.edu/~cruse/math109s06/tangents.ppt
if (c.isCircle()) {
GeoPoint point = this.getPoint();
GeoConic circle = this.getConic();
PVariable[] vPoint = point.getBotanaVars(point);
PVariable[] vcircle = circle.getBotanaVars(circle);
PPolynomial pointDistCircle = PPolynomial.equidistant(vPoint[0],
vPoint[1], vcircle[0], vcircle[1], vcircle[2],
vcircle[3]);
// is tangent point on circle?
if (isIntersectionPointIncident()) {
if (botanaVars == null) {
botanaVars = new PVariable[4];
// tangent point
botanaVars[0] = vPoint[0];
botanaVars[1] = vPoint[1];
// point on the tangent line
botanaVars[2] = new PVariable(kernel);
botanaVars[3] = new PVariable(kernel);
}
botanaPolynomials = new PPolynomial[3];
botanaPolynomials[0] = pointDistCircle;
// rotating the center of vcircle
// around vpoint by 90 degrees
// to get a point on the tangent line
botanaPolynomials[1] = new PPolynomial(botanaVars[1])
.subtract(new PPolynomial(vcircle[1]))
.subtract(new PPolynomial(botanaVars[2]))
.add(new PPolynomial(botanaVars[0]));
botanaPolynomials[2] = new PPolynomial(vcircle[0])
.subtract(new PPolynomial(botanaVars[0]))
.subtract(new PPolynomial(botanaVars[3]))
.add(new PPolynomial(botanaVars[1]));
return botanaPolynomials;
}
if (botanaVars == null) {
botanaVars = new PVariable[6];
// T - tangent point of circle
botanaVars[0] = new PVariable(kernel);
botanaVars[1] = new PVariable(kernel);
// A
botanaVars[2] = vPoint[0];
botanaVars[3] = vPoint[1];
// M - midpoint of OE
botanaVars[4] = new PVariable(kernel);
botanaVars[5] = new PVariable(kernel);
}
botanaPolynomials = new PPolynomial[4];
PPolynomial m1 = new PPolynomial(botanaVars[4]);
PPolynomial m2 = new PPolynomial(botanaVars[5]);
PPolynomial e1 = new PPolynomial(vPoint[0]);
PPolynomial e2 = new PPolynomial(vPoint[1]);
PPolynomial o1 = new PPolynomial(vcircle[0]);
PPolynomial o2 = new PPolynomial(vcircle[1]);
// M midpoint of EO
botanaPolynomials[0] = new PPolynomial(2).multiply(m1)
.subtract(o1).subtract(e1);
botanaPolynomials[1] = new PPolynomial(2).multiply(m2)
.subtract(o2).subtract(e2);
// MT = ME
botanaPolynomials[2] = PPolynomial.equidistant(botanaVars[0],
botanaVars[1], botanaVars[4], botanaVars[5], vPoint[0],
vPoint[1]);
// OT = OB
botanaPolynomials[3] = PPolynomial.equidistant(botanaVars[0],
botanaVars[1], vcircle[0], vcircle[1], vcircle[2],
vcircle[3]);
return botanaPolynomials;
}
if (c.isParabola()) {
GeoPoint point = this.getPoint();
GeoConic parabola = this.getConic();
PVariable[] vPoint = point.getBotanaVars(point);
PVariable[] vparabola = parabola.getBotanaVars(parabola);
// is tangent point on the parabola?
if (isIntersectionPointIncident()) {
if (botanaVars == null) {
botanaVars = new PVariable[4];
// M - midpoint of FT'
botanaVars[0] = new PVariable(kernel);
botanaVars[1] = new PVariable(kernel);
// T - tangent point
botanaVars[2] = vPoint[0];
botanaVars[3] = vPoint[1];
// the line MT will be the tangent
}
botanaPolynomials = new PPolynomial[4];
PPolynomial m1 = new PPolynomial(botanaVars[0]);
PPolynomial m2 = new PPolynomial(botanaVars[1]);
// coordinates of focus point of parabola
PPolynomial f1 = new PPolynomial(vparabola[8]);
PPolynomial f2 = new PPolynomial(vparabola[9]);
// coordinates of T' (feet point on the directrix for T)
PVariable t_1 = new PVariable(kernel);
PVariable t_2 = new PVariable(kernel);
PPolynomial t_1p = new PPolynomial(t_1);
PPolynomial t_2p = new PPolynomial(t_2);
// M midpoint of FT'
botanaPolynomials[0] = new PPolynomial(2).multiply(m1)
.subtract(f1).subtract(t_1p);
botanaPolynomials[1] = new PPolynomial(2).multiply(m2)
.subtract(f2).subtract(t_2p);
// T' is a feet point (we need to declare it)
botanaPolynomials[2] = PPolynomial.collinear(t_1, t_2,
vparabola[4], vparabola[5], vparabola[6],
vparabola[7]);
// TT' = TF
botanaPolynomials[3] = PPolynomial.equidistant(t_1, t_2,
vPoint[0], vPoint[1], vparabola[8], vparabola[9]);
return botanaPolynomials;
}
throw new NoSymbolicParametersException();
}
// Ellipse and hyperbola cannot be distinguished.
if (c.isEllipse() || c.isHyperbola()) {
GeoPoint point = this.getPoint();
GeoConic ellipse = this.getConic();
PVariable[] vPoint = point.getBotanaVars(point);
PVariable[] vellipse = ellipse.getBotanaVars(ellipse);
// is tangent point on the ellipse/hyperbola?
if (isIntersectionPointIncident()) {
if (botanaVars == null) {
botanaVars = new PVariable[6];
// M - tangent point
botanaVars[0] = new PVariable(kernel);
botanaVars[1] = new PVariable(kernel);
// T - point on ellipse/hyperbola
botanaVars[2] = vPoint[0];
botanaVars[3] = vPoint[1];
// D
botanaVars[4] = new PVariable(kernel);
botanaVars[5] = new PVariable(kernel);
}
botanaPolynomials = new PPolynomial[4];
PPolynomial m1 = new PPolynomial(botanaVars[0]);
PPolynomial m2 = new PPolynomial(botanaVars[1]);
// coordinates of second focus point of ellipse/hyperbola
PPolynomial f21 = new PPolynomial(vellipse[8]);
PPolynomial f22 = new PPolynomial(vellipse[9]);
// coordinates of D
PPolynomial d1 = new PPolynomial(botanaVars[4]);
PPolynomial d2 = new PPolynomial(botanaVars[5]);
// F_1,T,D collinear
botanaPolynomials[0] = PPolynomial.collinear(vellipse[6],
vellipse[7], vPoint[0], vPoint[1], botanaVars[4],
botanaVars[5]);
// F_2T = TD
botanaPolynomials[1] = PPolynomial.equidistant(vellipse[8],
vellipse[9], vPoint[0], vPoint[1], botanaVars[4],
botanaVars[5]);
// M midpoint of F_2D
botanaPolynomials[2] = new PPolynomial(2).multiply(m1)
.subtract(f21).subtract(d1);
botanaPolynomials[3] = new PPolynomial(2).multiply(m2)
.subtract(f22).subtract(d2);
return botanaPolynomials;
}
throw new NoSymbolicParametersException();
}
throw new NoSymbolicParametersException();
// TODO: implement the remaining cases
}
}