/*
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.
*/
/*
* AlgoCommonTangents.java, dsun48 [6/26/2011]
*
*/
package org.geogebra.common.kernel.algos;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
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.kernelND.AlgoIntersectND;
import org.geogebra.common.kernel.kernelND.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
/**
* Two tangents through point P to conic section c
*/
public abstract class AlgoCommonTangentsND extends AlgoElement
implements TangentAlgo {
protected GeoPointND[] P; // tmp
protected GeoConicND[] c; // input
protected GeoLineND[] tangents; // output
protected GeoLine polar, polar2;
protected AlgoIntersectND algoIntersect, algoIntersect2;
protected GeoPointND[] tangentPoints, tangentPoints2;
protected boolean equalLines = false, equalLines2 = false;
public AlgoCommonTangentsND(Construction cons, String[] labels,
GeoConicND c, GeoConicND c2) {
this(cons, c, c2);
GeoElement.setLabels(labels, getOutput());
}
AlgoCommonTangentsND(Construction cons, GeoConicND c, GeoConicND c2) {
super(cons);
this.c = new GeoConicND[2];
this.c[0] = c;
this.c[1] = c2;
double r = c.getCircleRadius();
double r2 = c2.getCircleRadius();
createPoints(cons);
// outer
if (Math.abs(r2 - r) > Kernel.MIN_PRECISION) {
setCoordsAsPoint(0, (c.b.getX() * r2 - c2.b.getX() * r) / (r2 - r),
(c.b.getY() * r2 - c2.b.getY() * r) / (r2 - r));
} else {
setCoordsAsVector(0, (c.b.getX() * r2 - c2.b.getX() * r),
(c.b.getY() * r2 - c2.b.getY() * r));
}
// the tangents are computed by intersecting the
// polar line of P with c
polar = new GeoLine(cons);
algoIntersect = createAlgo(cons, P[0], polar, this.c[0]);
// this is only an internal Algorithm that shouldn't be in the
// construction list
cons.removeFromConstructionList(algoIntersect);
tangentPoints = algoIntersect.getIntersectionPoints();
// inner
setCoordsAsPoint(1, (c.b.getX() * r2 + c2.b.getX() * r) / (r2 + r),
(c.b.getY() * r2 + c2.b.getY() * r) / (r2 + r));
// the tangents are computed by intersecting the
// polar line of P with c
polar2 = new GeoLine(cons);
algoIntersect2 = createAlgo(cons, P[1], polar2, this.c[1]);
// this is only an internal Algorithm that shouldn't be in the
// construction list
cons.removeFromConstructionList(algoIntersect2);
tangentPoints2 = algoIntersect2.getIntersectionPoints();
initTangents();
setInputOutput(); // for AlgoElement
compute();
// check if both lines are equal after creation:
// if they are equal we started with a point on the conic section
// in this case we only want to see one tangent line,
// so we make the second one undefined
equalLines = tangents[0].isEqual(tangents[1]);
if (equalLines) {
tangents[1].setUndefined();
tangentPoints[1].setUndefined();
}
equalLines2 = tangents[0 + 2].isEqual(tangents[1 + 2]);
if (equalLines2) {
tangents[1 + 2].setUndefined();
tangentPoints2[1].setUndefined();
}
}
/**
*
* @param cons1
* construction
* @return new point
*/
abstract protected void createPoints(Construction cons1);
/**
* set coords to the point (i.e z=1)
*
* @param index
* point index
* @param x
* x coord
* @param y
* y coord
* @param z
* z coord
*/
abstract protected void setCoordsAsPoint(int index, double x, double y);
/**
* set vector coords to the point (i.e z=0)
*
* @param index
* point index
* @param x
* x coord
* @param y
* y coord
* @param z
* z coord
*/
abstract protected void setCoordsAsVector(int index, double x, double y);
/**
* set tangents
*/
abstract protected void initTangents();
/**
*
* @param cons1
* construction
* @param p
* point
* @param line
* polar line
* @param conic
* conic
* @return intersect algo
*/
abstract protected AlgoIntersectND createAlgo(Construction cons1,
GeoPointND p, GeoLine line, GeoConicND conic);
@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] = c[0];
input[1] = c[1];
GeoElement[] out = new GeoElement[tangents.length];
for (int i = 0; i < tangents.length; i++) {
out[i] = (GeoElement) tangents[i];
}
super.setOutput(out);
setDependencies(); // done by AlgoElement
}
public GeoLineND[] getTangents() {
return tangents;
}
/**
*
* @return true if tangents will be undefined
*/
protected boolean checkUndefined() {
return !c[0].isCircle() || !c[1].isCircle();
}
/**
* @param csIndex
* coord sys index
* @param mpIndex
* midpoint index
* @return x coord for second midpoint
*/
abstract protected double getMidpointX(int csIndex, int mpIndex);
/**
* @param csIndex
* coord sys index
* @param mpIndex
* midpoint index
* @return y coord for second midpoint
*/
abstract protected double getMidpointY(int csIndex, int mpIndex);
// calc tangents
@Override
public final void compute() {
if (checkUndefined()) {
for (int i = 0; i < 4; i++) {
tangents[i].setUndefined();
}
return;
}
double r = c[0].getCircleRadius();
double r2 = c[1].getCircleRadius();
// outer
if (Math.abs(r2 - r) > Kernel.MIN_PRECISION) {
setCoordsAsPoint(0,
(getMidpointX(0, 0) * r2 - getMidpointX(0, 1) * r)
/ (r2 - r),
(getMidpointY(0, 0) * r2 - getMidpointY(0, 1) * r)
/ (r2 - r));
} else {
setCoordsAsVector(0,
(getMidpointX(0, 0) * r2 - getMidpointX(0, 1) * r),
(getMidpointY(0, 0) * r2 - getMidpointY(0, 1) * r));
}
// inner
setCoordsAsPoint(1,
(getMidpointX(1, 0) * r2 + getMidpointX(1, 1) * r) / (r2 + r),
(getMidpointY(1, 0) * r2 + getMidpointY(1, 1) * r) / (r2 + r));
// update polar lines
updatePolarLines();
// if P lies on the conic, the polar is a tangent
if (isIntersectionPointIncident(0, c[0])) {
setTangentFromPolar(0, polar);
tangentPoints[0].setCoordsFromPoint(P[0]);
// check if we had equal lines at the beginning
// if so we still don't want to see the second line
if (equalLines) {
tangents[1].setUndefined();
tangentPoints[1].setUndefined();
} else {
setTangentFromPolar(1, polar);
tangentPoints[1].setCoordsFromPoint(P[0]);
}
}
// if P is not on the conic, the tangents pass through
// the intersection points of polar and conic
else {
// intersect polar line with conic -> tangentPoints
algoIntersect.update();
// calc tangents through tangentPoints
updateTangents(tangentPoints, 0);
// we no longer have equal lines (if we ever had them)
equalLines = false;
}
// if P lies on the conic, the polar is a tangent
if (isIntersectionPointIncident(1, c[1])) {
setTangentFromPolar(0 + 2, polar2);
tangentPoints2[0].setCoordsFromPoint(P[1]);
// check if we had equal lines at the beginning
// if so we still don't want to see the second line
if (equalLines2) {
tangents[1 + 2].setUndefined();
tangentPoints2[1].setUndefined();
} else {
setTangentFromPolar(1 + 2, polar2);
tangentPoints2[1].setCoordsFromPoint(P[1]);
}
}
// if P is not on the conic, the tangents pass through
// the intersection points of polar and conic
else {
// intersect polar line with conic -> tangentPoints
algoIntersect2.update();
// calc tangents through tangentPoints
updateTangents(tangentPoints2, 1);
// we no longer have equal lines (if we ever had them)
equalLines2 = false;
}
} // end of compute
/**
* update polar lines (both)
*/
abstract protected void updatePolarLines();
/**
* @param tangentPts
* tangent points
* @param index
* first/second tangents
*/
abstract protected void updateTangents(GeoPointND[] tangentPts,
int index);
/**
* set tangent equal to polar
*
* @param i
* tangent index
* @param line
* polar line
*/
abstract protected void setTangentFromPolar(int i, GeoLine line);
/**
*
* @param index
* point index
* @param conic
* conic
* @return true if point is on conic
*/
abstract protected boolean isIntersectionPointIncident(int index,
GeoConicND conic);
@Override
public GeoPointND getTangentPoint(GeoElement conic, GeoLine line) {
if (conic != c[0] && conic != c[1]) {
return null;
}
if (conic == c[0]) {
if (line == tangents[0]) {
return tangentPoints[0];
} else if (line == tangents[1]) {
return tangentPoints[1];
} else {
return null;
}
} else if (conic == c[1]) {
if (line == tangents[2]) {
return tangentPoints2[0];
} else if (line == tangents[3]) {
return tangentPoints2[1];
} else {
return null;
}
} else {
return null;
}
}
@Override
public final String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-30
// simplified to allow better Chinese translation
return getLoc().getPlain("CommonTangentOfCirclesAandB",
c[0].getLabel(tpl), c[1].getLabel(tpl));
}
}
// Local Variables:
// indent-tabs-mode: nil
// c-basic-offset: 4
// tab-width: 4
// End:
// vim: set expandtab shiftwidth=4 softtabstop=4 tabstop=4