package org.geogebra.common.kernel.algos;
import java.util.ArrayList;
import org.geogebra.common.euclidian.EuclidianConstants;
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.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.prover.NoSymbolicParametersException;
import org.geogebra.common.kernel.prover.polynomial.PPolynomial;
import org.geogebra.common.kernel.prover.polynomial.PVariable;
/**
* Single intersection point
*/
public class AlgoIntersectSingle extends AlgoIntersect implements
SymbolicParametersBotanaAlgo {
// input
private AlgoIntersect algo;
private GeoNumberValue index; // index of point in algo, can be input
// directly or be
// calculated from refPoint
private GeoPoint refPoint; // reference point in algo to calculate index;
// can be null or undefined
// output
private GeoPoint point;
private GeoPoint[] parentOutput;
private int idx;
private PPolynomial[] botanaPolynomials;
private PVariable[] botanaVars;
/**
* Creates algo for single intersection close to given point
*
* @param label
* label for output
* @param algo
* intersection algo with multiple outputs
* @param refPoint
* point close to desired intersection
*/
public AlgoIntersectSingle(String label, AlgoIntersect algo,
GeoPoint refPoint) {
super(algo.cons);
this.algo = algo;
algo.addUser(); // this algorithm is a user of algo
this.refPoint = refPoint;
point = new GeoPoint(algo.cons);
setInputOutput();
initForNearToRelationship();
compute();
point.setLabel(label);
addIncidence();
}
/**
* Creates algo for single intersection with given index
*
* @param label
* label for output
* @param algo
* intersection algo with multiple outputs
* @param index
* index, starting with 1
*/
public AlgoIntersectSingle(String label, AlgoIntersect algo,
GeoNumberValue index) {
super(algo.cons);
this.algo = algo;
algo.addUser(); // this algorithm is a user of algo
// check index
this.index = index;
refPoint = null;
point = new GeoPoint(algo.cons);
setInputOutput();
initForNearToRelationship();
compute();
// setIncidence();
point.setLabel(label);
addIncidence();
}
/**
* Creates algo for single intersection with given index
*
* @param label
* label for output
* @param algo
* intersection algo with multiple outputs
* @param index
* index, starting with 0
*/
public AlgoIntersectSingle(String label, AlgoIntersect algo, int index) {
this(label, algo, new GeoNumeric(algo.getConstruction(), index + 1));
}
private void addIncidence() {
// point's incidence with parent algo's two intersectable objects
if (algo instanceof AlgoIntersectConics) {
point.addIncidence(((AlgoIntersectConics) algo).getA(), false);
point.addIncidence(((AlgoIntersectConics) algo).getB(), false);
// these two lines are already done in point.addIncidence()
// ((GeoConic)
// ((AlgoIntersectConics)algo).getA()).addPointOnConic(point);
// ((GeoConic)
// ((AlgoIntersectConics)algo).getB()).addPointOnConic(point);
} else if (algo instanceof AlgoIntersectLineConic) {
point.addIncidence(((AlgoIntersectLineConic) algo).getLine(),
false);
point.addIncidence(((AlgoIntersectLineConic) algo).getConic(),
false);
// this is already done in point.addIncidence()
// ((AlgoIntersectLineConic)algo).getConic().addPointOnConic(point);
}
// points's incidence with one of the intersection points --
// this is done in compute(), because it depends on the index, which can
// be changed dynamically
}
@Override
protected boolean showUndefinedPointsInAlgebraView() {
return true;
}
@Override
public Commands getClassName() {
return Commands.Intersect;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_INTERSECT;
}
// for AlgoElement
@Override
public void setInputOutput() {
if (refPoint == null) {
input = new GeoElement[3];
input[0] = algo.input[0];
input[1] = algo.input[1];
// dummy value to store the index of the intersection point
// index + 1 is used here to let numbering start at 1
input[2] = index.toGeoElement();
} else {
input = new GeoElement[3];
input[0] = algo.input[0];
input[1] = algo.input[1];
input[2] = refPoint;
}
super.setOutputLength(1);
super.setOutput(0, point);
setDependencies(); // done by AlgoElement
}
/**
* Added for LocusEqu
*
* @return inner algo.
*/
public AlgoIntersect getAlgo() {
return this.algo;
}
/**
* @return resulting point
*/
public GeoPoint getPoint() {
return point;
}
@Override
public GeoPoint[] getIntersectionPoints() {
return (GeoPoint[]) super.getOutput();
}
@Override
protected GeoPoint[] getLastDefinedIntersectionPoints() {
return null;
}
@Override
public boolean isNearToAlgorithm() {
return true;
}
@Override
public final void initForNearToRelationship() {
parentOutput = algo.getIntersectionPoints();
// tell parent algorithm about the loaded position;
// this is needed for initing the intersection algo with
// the intersection point stored in XML files
algo.initForNearToRelationship();
algo.setIntersectionPoint(idx, point);
algo.compute();
}
@Override
public void compute() {
if (index != null) {
idx = Math.max(0, (int) index.getDouble() - 1);
}
parentOutput = algo.getIntersectionPoints();
if (kernel.getLoadingMode() && point.hasUpdatePrevilege) { // for
// backward
// compatability
algo.setIntersectionPoint(idx, point);
point.hasUpdatePrevilege = false;
}
// update index if reference point has been defined
if (refPoint != null) {
if (refPoint.isDefined()) {
idx = algo.getClosestPointIndex(refPoint);
}
}
if (input[0].isDefined() && input[1].isDefined()
&& idx < parentOutput.length) {
// get coordinates from helper algorithm
point.setCoords(parentOutput[idx]);
if (point.getIncidenceList() != null) {
for (int i = 0; i < parentOutput.length; ++i) {
if (!parentOutput[idx].contains(parentOutput[i])) {
point.getIncidenceList().remove(parentOutput[i]);
}
}
}
point.addIncidence(parentOutput[idx], false);
} else {
point.setUndefined();
ArrayList<GeoElement> al = point.getIncidenceList();
if (al != null) {
for (int i = 0; i < parentOutput.length; ++i) {
al.remove(parentOutput[i]);
}
}
}
}
@Override
public void remove() {
super.remove();
algo.removeUser(); // this algorithm was a user of algo
}
@Override
public String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-30
// simplified to allow better Chinese translation
if (refPoint == null) {
return getLoc().getPlain("IntersectionPointOfAB",
input[0].getLabel(tpl), input[1].getLabel(tpl));
}
return getLoc().getPlain("IntersectionPointOfABNearC",
input[0].getLabel(tpl), input[1].getLabel(tpl),
input[2].getLabel(tpl));
}
@Override
public PVariable[] getBotanaVars(GeoElementND geo) {
return botanaVars;
}
@Override
public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
throws NoSymbolicParametersException {
if (botanaPolynomials != null) {
return botanaPolynomials;
}
if (algo != null) {
if (algo instanceof AlgoIntersectLineConic) {
botanaPolynomials = ((SymbolicParametersBotanaAlgo) algo)
.getBotanaPolynomials(geo);
if (botanaVars == null) {
botanaVars = ((SymbolicParametersBotanaAlgo) algo)
.getBotanaVars(geo);
}
}
if (algo instanceof AlgoIntersectConics) {
botanaPolynomials = ((SymbolicParametersBotanaAlgo) algo)
.getBotanaPolynomials(geo);
if (botanaVars == null) {
botanaVars = ((SymbolicParametersBotanaAlgo) algo)
.getBotanaVars(geo);
}
}
return botanaPolynomials;
}
throw new NoSymbolicParametersException();
}
}