/*
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.
*/
/*
* AlgoIntersectLineConic.java
*
* Created on 30. August 2001, 21:37
*/
package org.geogebra.common.kernel.algos;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.PointPairList;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoConic;
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.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoElementND;
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;
/**
*
* @author Markus
*/
public class AlgoIntersectLineConic extends AlgoIntersect implements
SymbolicParametersBotanaAlgo {
/** input line */
protected GeoLine g;
/** input conic */
protected GeoConic c;
private GeoPoint[] D; // D: old points; Q: new points, not yet permuted
protected GeoPoint[] P, Q; // output -- Q permuted according to D
protected int intersectionType;
private HashMap<GeoElementND, PPolynomial[]> botanaPolynomials;
private HashMap<GeoElementND, PVariable[]> botanaVars;
private int age[]; // of defined points D
private int permutation[]; // of computed intersection points Q to output
// points P
private double[][] distTable;
private boolean isQonPath[]; // for every new intersection point Q: is it on
// both paths?
// for every resulting point P: has it ever been defined, i.e. is it alive?
private boolean isPalive[];
// private int i;
private boolean isDefinedAsTangent;
private boolean firstIntersection = true;
private boolean isPermutationNeeded = true;
private GeoPointND tangentPoint;
private PointPairList pointList = new PointPairList();
// for segments, rays and conic parts we need to check the
// intersection points at the end of compute()
private boolean isLimitedPathSituation;
protected boolean possibleSpecialCase = false;
protected int specialCasePointOnCircleIndex = 0; // index of point on line
// and conic
private GeoPointND existingIntersection = null;
@Override
public Commands getClassName() {
return Commands.Intersect;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_INTERSECT;
}
public AlgoIntersectLineConic(Construction cons, GeoLine g, GeoConic c) {
super(cons);
this.g = g;
this.c = c;
isLimitedPathSituation = g.isLimitedPath() || c.isLimitedPath();
// check special cases
// if g is defined as a tangent of c, we dont't need
// to compute anything
if (g.getParentAlgorithm() instanceof TangentAlgo) {
TangentAlgo algo = (TangentAlgo) g.getParentAlgorithm();
tangentPoint = algo.getTangentPoint(c, g);
isDefinedAsTangent = (tangentPoint != null);
}
initElements();
setInputOutput(); // for AlgoElement
initForNearToRelationship();
compute();
addIncidence(); // must be after compute()
}
/**
* @author Tam
*
* for special cases of e.g. AlgoIntersectLineConic
*/
private void addIncidence() {
for (int i = 0; i < P.length; ++i) {
P[i].addIncidence(g, false);
P[i].addIncidence(c, false);
}
}
// for subclasses
protected void initElements() {
// g is defined as tangent of c
if (isDefinedAsTangent) {
P = new GeoPoint[1];
P[0] = new GeoPoint(cons);
// Q and D are not defined here
}
// standard case
else {
P = new GeoPoint[2];
D = new GeoPoint[2];
Q = new GeoPoint[2];
distTable = new double[2][2];
age = new int[2];
permutation = new int[2];
isQonPath = new boolean[2];
isPalive = new boolean[2];
for (int i = 0; i < 2; i++) {
Q[i] = new GeoPoint(cons);
P[i] = new GeoPoint(cons);
D[i] = new GeoPoint(cons);
}
// check possible special case
possibleSpecialCase = handleSpecialCase();
}
}
// for AlgoElement
@Override
public void setInputOutput() {
input = new GeoElement[2];
input[0] = c;
input[1] = g;
super.setOutput(P);
noUndefinedPointsInAlgebraView();
setDependencies(); // done by AlgoElement
}
@Override
public final GeoPoint[] getIntersectionPoints() {
return P;
}
// Made public for LocusEqu
public GeoLine getLine() {
return g;
}
// Made public for LocusEqu
public GeoConic getConic() {
return c;
}
@Override
protected GeoPoint[] getLastDefinedIntersectionPoints() {
return D;
}
@Override
public boolean isNearToAlgorithm() {
return true;
}
@Override
public final void initForNearToRelationship() {
if (isDefinedAsTangent) {
return;
}
isPermutationNeeded = true; // for non-continuous intersections
for (int i = 0; i < P.length; i++) {
age[i] = 0;
isQonPath[i] = true;
isPalive[i] = false;
}
}
// calc intersections of conic c and line g
@Override
public void compute() {
/*
* no probabilistic checking anymore, see #1044
*/
// within addIncidenceWithProbabilisticChecking(), updateCascade() is
// called
// and we don't what this.compute() to be invoked repeatedly.
/*
* if (handlingSpecialCase) return;
*/
// g is defined as tangent of c
if (isDefinedAsTangent) {
P[0].setCoordsFromPoint(tangentPoint);
return;
}
// check for special case of line through point on conic
if (possibleSpecialCase) {
if (handleSpecialCase()) {
return;
}
}
// continous: use near-to-heuristic between old and new intersection
// points
// non-continous: use computeContinous() to init a permutation and then
// always use this permutation
boolean continous = isPermutationNeeded || kernel.isContinuous()
|| kernel.getLoadingMode();
if (continous) {
computeContinuous();
} else {
computeNonContinuous();
}
avoidDoubleTangentPoint();
}
/**
* There is an important special case we handle separately: The conic
* section c is intersected with a line passing through a point A on c. In
* this case the first intersection point should always be A.
*
* Definition of special case: There is a pre-exist point which is both
* dependent and incident to the conic and the line.
*
* i.e. there is a special case if and only if there exists one point S such
* that (1) the conic was constructed through S, or S is already some
* intersection point of the conic with other object; (2) the line was
* constructed through S, or S is already some intersection point of the
* line with other object.
*
* Therefore, "addIncidence()" should be called in the following Algos:
* AlgoJoinPoints, AlgoJoinPointsRay, AlgoJoinPointsSegment
* AlgoLinePointLine, AlgoLinePointVector, AlgoOrthoLinePointLine,
* AlgoOrthoLinePointVector, AlgoOrthoLinePointConic AlgoConicFivePoints,
* AlgoEllipseFociPoint, AlgoHyperbolaFociPoint AlgoIntersectLineXXX,
* AlgoIntersectXXXLine AlgoIntersectConicXXX, AlgoIntersectXXXConic
* AlgoPointOnPath GeoLine.setStartPoint, GeoLine.setEndPoint
*
* @return true if this special case was handled.
*/
private boolean handleSpecialCase() {
// Numerical check does not work, because when a point incidentally
// stands on g and c, it may not be considered
// as special case
/*
* if (g.startPoint != null && c.isOnPath(g.startPoint,
* AbstractKernel.MIN_PRECISION)) { pointOnConic = g.startPoint; } else
* if (g.endPoint != null && c.isOnPath(g.endPoint,
* AbstractKernel.MIN_PRECISION)) { pointOnConic = g.endPoint; } else {
* // get points on conic and see if one of them is on line g ArrayList
* pointsOnConic = c.getPointsOnConic(); if (pointsOnConic != null) {
* int size = pointsOnConic.size(); for (int i=0; i < size; i++) {
* GeoPoint p = (GeoPoint) pointsOnConic.get(i); if (g.isOnPath(p,
* AbstractKernel.MIN_PRECISION)) { pointOnConic = p; break; } } } }
*/
existingIntersection = null;
// find a point from conic c on line g
ArrayList<GeoPointND> pointsOnConic = c.getPointsOnConic();
if (pointsOnConic != null) {
// get a point from pointsOnConic to see if it is on g.
for (int i = 0; i < pointsOnConic.size(); ++i) {
GeoPointND p = pointsOnConic.get(i);
if (p.isLabelSet()) { // an existing intersection should be a
// labeled one
if (p.getIncidenceList() != null
&& p.getIncidenceList().contains(g)) {
// TODO: this is just a TEMPORARY FIX for #94.
// if (g.isOnPath(p, Kernel.EPSILON)
// && c.isOnPath(p, Kernel.EPSILON))
// existingIntersection = p;
existingIntersection = p;
break;
} /*
* else if (!(p.getNonIncidenceList() != null && p //no
* probabilistic checking anymore. See #1044
* .getNonIncidenceList().contains(g)) &&
* p.addIncidenceWithProbabilisticChecking(g) ) {
* existingIntersection = p; break; }
*/
}
}
}
// if existingIntersection is still not found, find a point from line g
// on conic c
if (existingIntersection == null) {
if (handleSpecialCasePoint(g.getStartPoint())) {
existingIntersection = g.getStartPoint();
} else if (handleSpecialCasePoint(g.getEndPoint())) {
existingIntersection = g.getEndPoint();
}
}
if (existingIntersection == null) {
ArrayList<GeoPoint> pointsOnLine = g.getPointsOnLine();
if (pointsOnLine != null) {
// get a point from pointsOnLine to see if it is on c.
for (int i = 0; i < pointsOnLine.size(); ++i) {
if (handleSpecialCasePoint(pointsOnLine.get(i))) {
existingIntersection = pointsOnLine.get(i);
break;
}
}
}
}
// TODO: maybe there's a point neither from conic c nor from line g that
// is an existing intersection!
// efficient algorithm for this might only rely on automatic proving
// if existingIntersection is still not found, report no special case
// handled
if (existingIntersection == null) {
return false;
}
// calc new intersection points Q
intersect(c, Q);
// pointOnConic should be first intersection point
// Note: if the first intersection point was already set when a file
// was loaded, then we need to make sure that we don't lose this
// information
int firstIndex = specialCasePointOnCircleIndex;
int secondIndex = 1 - firstIndex;
if (firstIntersection && didSetIntersectionPoint(firstIndex)) {
if (!P[firstIndex].isEqual(existingIntersection)) {
// pointOnConic is NOT equal to the loaded intersection point:
// we need to swap the indices
int temp = firstIndex;
firstIndex = secondIndex;
secondIndex = temp;
specialCasePointOnCircleIndex = firstIndex;
}
firstIntersection = false;
}
// pointOnConic should be first intersection point
P[firstIndex].setCoordsFromPoint(existingIntersection);
// the other intersection point should be the second one
boolean didSetP1 = false;
for (int i = 0; i < 2; i++) {
if (!Q[i].isEqual(P[firstIndex])) {
P[secondIndex].setCoords(Q[i]);
didSetP1 = true;
break;
}
}
if (!didSetP1) {
P[secondIndex].setCoordsFromPoint(existingIntersection);
}
if (isLimitedPathSituation) {
// make sure the points are on a limited path
for (int i = 0; i < 2; i++) {
if (!pointLiesOnBothPaths(P[i])) {
P[i].setUndefined();
}
}
}
return true;
}
private boolean handleSpecialCasePoint(GeoPoint p) {
if (p == null) {
return false;
}
if (p.isLabelSet()) { // an existing intersection should be
// a labeled one
if (p.getIncidenceList() != null
&& p.getIncidenceList().contains(c)) {
// TODO: this is just a TEMPORARY FIX for #94.
// if (g.isOnPath(p, Kernel.EPSILON)
// && c.isOnPath(p, Kernel.EPSILON))
// existingIntersection = p;
return true;
} /*
* else if (p.addIncidenceWithProbabilisticChecking(c)) { //no
* probabilistic checking anymore. See #1044
* existingIntersection = p; AbstractApplication.debug(p);
* break; }
*/
}
return false;
}
/**
* Use the current permutation to set output points P from computed points
* Q.
*/
private void computeNonContinuous() {
// calc new intersection points Q
intersect(c, Q);
// use fixed permutation to set output points P
for (int i = 0; i < P.length; i++) {
P[i].setCoords(Q[permutation[i]]);
}
if (isLimitedPathSituation) {
// make sure the points are on a limited path
for (int i = 0; i < P.length; i++) {
if (!pointLiesOnBothPaths(P[i])) {
P[i].setUndefined();
}
}
}
}
/**
* We want to find a permutation of Q, so that the distances between old
* points Di and new points Qi are minimal.
*/
private void computeContinuous() {
/*
* D ... old defined points P ... current points Q ... new points
*
* We want to find a permutation of Q, so that the distances between old
* point Di and new Point Qi are minimal.
*/
// remember the defined points D, so that Di = Pi if Pi is defined
// and set age
boolean noSingularity = !P[0].isEqual(P[1]); // singularity check
for (int i = 0; i < 2; i++) {
boolean finite = P[i].isFinite();
// don't do this if P[0] = P[1]
if (noSingularity && finite) {
D[i].setCoords(P[i]);
age[i] = 0;
} else {
age[i]++;
}
// update alive state
isPalive[i] = isPalive[i] || finite || P[i].isLabelSet();
}
// calc new intersection points Q
intersect(c, Q);
// for limited paths we have to distinguish between intersection points
// Q
// that lie on both limited paths or not. This is important for choosing
// the right permutation in setNearTo()
if (isLimitedPathSituation) {
updateQonPath();
}
if (firstIntersection) {
// init points in order P[0], P[1]
int count = 0;
for (int i = 0; i < Q.length; i++) {
// make sure interesection points lie on limited paths
if (Q[i].isDefined() && pointLiesOnBothPaths(Q[i])) {
P[count].setCoords(Q[i]);
D[count].setCoords(P[count]);
count++;
}
// TRAC-4272 don't initialize nearto relations until we actually
// have two intersections
if (count > 1 && !P[0].isEqual(P[1])) {
firstIntersection = false;
}
}
// TRAC-643 we may have P loaded from XML on redfine
for (int i = count; i < P.length; i++) {
P[i].setUndefined();
}
return;
}
// calc distance table
AlgoIntersectConics.distanceTable(D, age, Q, distTable);
// find permutation and calculate new mean distances
AlgoIntersectConics.setNearTo(P, isPalive, Q, isQonPath, distTable,
pointList, permutation, !isPermutationNeeded, 0.000001);
isPermutationNeeded = false;
/*
* System.out.print("permutation: "); for (int i=0; i <
* permutation.length; i++) { System.out.print(permutation[i] + " "); }
* Application.debug();
*/
// make sure interesection points lie on limited paths
if (isLimitedPathSituation) {
handleLimitedPaths();
}
}
/**
* Checks whether the computed intersection points really lie on the limited
* paths. Note: points D[] and P[] may be changed here.
*/
private void handleLimitedPaths() {
// singularity check
boolean noSingularity = !P[0].isEqual(P[1]);
for (int i = 0; i < P.length; i++) {
if (P[i].isDefined()) {
if (!pointLiesOnBothPaths(P[i])) {
// the intersection point should be undefined as it doesn't
// lie
// on both (limited) paths. However, we want to keep the
// information
// of P[i]'s position for our near-to-approach to achieve
// continous movements.
// That's why we remember D[i] now
if (noSingularity && P[i].isFinite()) {
D[i].setCoords(P[i]);
// the age will be increased by 1 at the
// next call of compute() as P[i] will be undefined
age[i] = -1;
}
P[i].setUndefined();
}
}
}
}
/**
* Checks wether Q[i] lies on g and c and sets isQonPath[] accordingly.
*/
private void updateQonPath() {
for (int i = 0; i < Q.length; i++) {
isQonPath[i] = pointLiesOnBothPaths(Q[i]);
}
}
private boolean pointLiesOnBothPaths(GeoPoint P) {
return g.isIntersectionPointIncident(P, Kernel.MIN_PRECISION)
&& c.isIntersectionPointIncident(P, Kernel.MIN_PRECISION);
}
// INTERSECTION TYPES
public static final int INTERSECTION_PRODUCING_LINE = 1;
public static final int INTERSECTION_ASYMPTOTIC_LINE = 2;
public static final int INTERSECTION_MEETING_LINE = 3;
public static final int INTERSECTION_TANGENT_LINE = 4;
public static final int INTERSECTION_SECANT_LINE = 5;
public static final int INTERSECTION_PASSING_LINE = 6;
/**
* Intersects conic c with line g and always sets two GeoPoints (sol). If
* there are no real intersections, the coords of GeoPoints are set to
* Double.NaN.
*
* Also store the intersection type.
*
* @returns type of intersection
*/
private int intersect(GeoConic c, GeoPoint[] sol) {
boolean ok = false;
int ret = INTERSECTION_PASSING_LINE;
if (c.isDefined() && g.isDefined()) {
double epsilon = 1E-15;
while (epsilon <= Kernel.MIN_PRECISION) {
ret = intersectLineConic(g, c, sol, epsilon);
ok = testPoints(g, c, sol, Kernel.MIN_PRECISION);
if (ok) {
break;
}
epsilon *= 10.0;
}
}
// intersection failed
if (!ok) {
// Application.debug("INTERSECT LINE CONIC FAILED: epsilon = " +
// epsilon);
for (int i = 0; i < 2; i++) {
sol[i].setUndefined();
}
}
intersectionType = ret;
return ret;
}
// not initializing this is important for performance
private static double[] xyz = new double[3];
// do the actual computations
public final static synchronized int intersectLineConic(GeoLine g,
GeoConicND c, GeoPoint[] sol, double eps) {
double[] A = c.getFlatMatrix();
g.getnormalizedCoefficients(xyz);
double x = xyz[0];
double y = xyz[1];
double z = xyz[2];
// get arbitrary point of line
double px, py;
if (Math.abs(x) > Math.abs(y)) {
px = -z / x;
py = 0.0d;
} else {
px = 0.0d;
py = -z / y;
}
// we have to solve u t? + 2d t + w = 0
// to intersect line g: X = p + t v with conic
// calc u, d, w:
// u = v.S.v (S is upper left submatrix of A)
// d = p.S.v + a.v
// w = evaluate(p)
// dis = d^2 - uw, err(dis) = 2d err(d) - u err(w) - w err(u)
// for simplicity, suppose err(d), err(w), err(u) <= epsilon, then delta
// = err(dis) <= (|2d|+|u|+|w|)epsilon
// precalc S.v for u and d
double SvX = A[0] * y - A[3] * x;
double SvY = A[3] * y - A[1] * x;
double u = y * SvX - x * SvY;
double d = px * SvX + py * SvY + A[4] * y - A[5] * x;
double w = c.evaluate(px, py);
// estimate err for delta; also avoid this too be too large
double delta = Math.min(Kernel.MIN_PRECISION,
Math.max(1, Math.abs(2 * d) + Math.abs(u) + Math.abs(w)) * eps);
// Erzeugende, Asymptote oder Treffgerade
if (Kernel.isZero(u, eps)) {
// Erzeugende oder Asymptote
if (Kernel.isZero(d, eps)) {
// Erzeugende
if (Kernel.isZero(w, eps)) {
sol[0].setUndefined();
sol[1].setUndefined();
return INTERSECTION_PRODUCING_LINE;
}
// Asymptote
// w != 0
sol[0].setUndefined();
sol[1].setUndefined();
return INTERSECTION_ASYMPTOTIC_LINE;
}
// Treffgerade
// d != 0
double t0 = -w / (2.0 * d);
if (d < 0) {
sol[0].setCoords(px + t0 * y, py - t0 * x, 1.0d);
sol[1].setUndefined();
} else { // d > 0
sol[0].setUndefined();
sol[1].setCoords(px + t0 * y, py - t0 * x, 1.0d);
}
return INTERSECTION_MEETING_LINE;
}
// Tangente, Sekante, Passante
// u != 0
double dis = d * d - u * w;
// Tangente
// if (AbstractKernel.isZero(dis)) {
if (Kernel.isEqual(dis, 0, delta)) {
double t1 = -d / u;
sol[0].setCoords(px + t1 * y, py - t1 * x, 1.0);
sol[1].setCoords(sol[0]);
return INTERSECTION_TANGENT_LINE;
}
// Sekante oder Passante
// Sekante
// Double line => one intersection point
if (c.type == GeoConicNDConstants.CONIC_DOUBLE_LINE) {
double t1 = -d / u;
sol[0].setCoords(px + t1 * y, py - t1 * x, 1.0);
sol[1].setCoords(sol[0]);
return INTERSECTION_SECANT_LINE;
}
if (dis > 0) {
dis = Math.sqrt(dis);
// For accuracy, calculate one root using:
// (-d +/- dis) / u
// and the other using:
// w / (-d +/- dis)
// Choose the sign of the +/- so that d+dis gets larger in
// magnitude
boolean swap = d < 0.0;
if (swap) {
dis = -dis;
}
double q = -(d + dis);
double t1 = swap ? w / q : q / u;
double t2 = swap ? q / u : w / q;
sol[0].setCoords(px + t1 * y, py - t1 * x, 1.0);
sol[1].setCoords(px + t2 * y, py - t2 * x, 1.0);
return INTERSECTION_SECANT_LINE;
}
// Passante
// dis < 0
sol[0].setUndefined();
sol[1].setUndefined();
return INTERSECTION_PASSING_LINE;
}
/**
* Tests if at least one point lies on conic c and line g.
*/
final static private boolean testPoints(GeoLine g, GeoConic c, GeoPoint[] P,
double eps) {
boolean foundPoint = false;
for (int i = 0; i < P.length; i++) {
if (P[i].isDefined()) {
if (!(c.isOnFullConic(P[i], eps) && g.isOnFullLine(P[i], eps))) {
P[i].setUndefined();
} else {
foundPoint = true;
}
}
}
return foundPoint;
}
@Override
public PVariable[] getBotanaVars(GeoElementND geo) {
return botanaVars.get(geo);
}
@Override
public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
throws NoSymbolicParametersException {
if (botanaPolynomials != null) {
PPolynomial[] ret = botanaPolynomials.get(geo);
if (ret != null) {
return ret;
}
}
// We cannot decide a statement yet if the conic is not a circle.
/*
* In fact we cannot decide a statement properly if the "line" is a
* segment, at least not algebraically (without using cylindrical
* algebraic decomposition or such). But since we are doing constructive
* geometry, it's better to assume that segment/circle intersection is
* not a real problem. TODO: Consider adding an NDG somehow in this case
* (but maybe not really important and useful).
*
* See also AlgoIntersectLines.
*/
if (c.isCircle()) {
if (g != null /* !g.isGeoSegment() && */) {
PVariable[] botanaVarsThis = new PVariable[2];
if (botanaVars == null) {
botanaVars = new HashMap<GeoElementND, PVariable[]>();
}
if (botanaVars.containsKey(geo)) {
botanaVarsThis = botanaVars.get(geo);
} else {
/*
* Intersection point (we create only one here, the other
* one will be created by the other geo's algo):
*/
botanaVarsThis = new PVariable[2];
botanaVarsThis[0] = new PVariable(kernel);
botanaVarsThis[1] = new PVariable(kernel);
botanaVars.put(geo, botanaVarsThis);
}
/*
* If this point is not shown, then force a criterion that the
* symbolic intersection must differ from that point. See below.
*/
int excludePoint = 0;
if (!this.isInConstructionList()
&& existingIntersections() != 0) {
/*
* This case is present if we explicitly point to one
* intersection point of a line and a circle. If the line
* and the circle already have a common point, then the user
* may point to the other intersection point. In this case
* we explicitly claim that the intersection point differs
* from the common point.
*/
excludePoint = 1;
}
PPolynomial[] botanaPolynomialsThis = null;
/*
* Force a criterion that the two intersection points must
* differ: See page 150 in Zoltan's diss, 1st paragraph. TODO:
* This is very ugly.
*/
PVariable[] botanaVarsOther = new PVariable[2];
Iterator<Entry<GeoElementND, PVariable[]>> it = botanaVars
.entrySet().iterator();
boolean found = false;
while (it.hasNext()) {
Entry<GeoElementND, PVariable[]> entry = it.next();
GeoElementND otherGeo = entry.getKey();
/*
* This should be at most one element. There is one element
* if we found the second intersection point, otherwise (for
* the first intersection point) there is no otherGeo yet,
* so we will not create any polynomials here (yet).
*/
if (!otherGeo.equals(geo)) {
botanaPolynomialsThis = new PPolynomial[3
+ excludePoint];
botanaVarsOther = entry.getValue();
botanaPolynomialsThis[2 + excludePoint] = (PPolynomial
.sqrDistance(botanaVarsThis[0],
botanaVarsThis[1], botanaVarsOther[0],
botanaVarsOther[1])
.multiply(new PPolynomial(new PVariable(kernel))))
.subtract(new PPolynomial(1));
found = true;
}
}
if (!found) {
botanaPolynomialsThis = new PPolynomial[2 + excludePoint];
}
PVariable[] vg = g.getBotanaVars(g); // 4 variables from the
// line
PVariable[] vc = c.getBotanaVars(c); // 4 variables from the
// circle
botanaPolynomialsThis[0] = PPolynomial.collinear(vg[0], vg[1],
vg[2], vg[3], botanaVarsThis[0], botanaVarsThis[1]);
botanaPolynomialsThis[1] = PPolynomial.equidistant(vc[2], vc[3],
vc[0], vc[1], botanaVarsThis[0], botanaVarsThis[1]);
if (botanaPolynomials == null) {
botanaPolynomials = new HashMap<GeoElementND, PPolynomial[]>();
}
/*
* If this point is not shown, then force a criterion that the
* symbolic intersection must differ from that point. See above.
*/
if (excludePoint > 0) {
botanaVarsOther = ((GeoPoint) existingIntersection)
.getBotanaVars(existingIntersection);
botanaPolynomialsThis[botanaPolynomialsThis.length
- 1] = (PPolynomial.sqrDistance(botanaVarsThis[0],
botanaVarsThis[1], botanaVarsOther[0],
botanaVarsOther[1])
.multiply(new PPolynomial(
new PVariable(kernel))))
.subtract(new PPolynomial(1));
}
botanaPolynomials.put(geo, botanaPolynomialsThis);
/*
* TODO: We created the botanaPolynomials by building up an
* array here from at most three parts. It would be nicer to do
* it in a more sophisticated way.
*/
return botanaPolynomialsThis;
}
throw new NoSymbolicParametersException();
} else if (c.isParabola() || c.isEllipse() || c.isHyperbola()) {
if (g != null) {
PVariable[] vg = g.getBotanaVars(g);
PVariable[] botanaVarsThis = new PVariable[2];
if (botanaVars == null) {
botanaVars = new HashMap<GeoElementND, PVariable[]>();
}
if (botanaVars.containsKey(geo)) {
botanaVarsThis = botanaVars.get(geo);
} else {
botanaVarsThis = new PVariable[2];
botanaVarsThis[0] = new PVariable(kernel);
botanaVarsThis[1] = new PVariable(kernel);
botanaVars.put(geo, botanaVarsThis);
}
PPolynomial[] conicPolys = c.getBotanaPolynomials(c);
PVariable[] conicVars = c.getBotanaVars(c);
int conicPolysNo = conicPolys.length;
PPolynomial[] botanaPolynomialsThis = new PPolynomial[conicPolysNo
+ 1];
for (int i = 0; i < conicPolysNo; i++) {
botanaPolynomialsThis[i] = conicPolys[i]
.substitute(conicVars[0], botanaVarsThis[0])
.substitute(conicVars[1], botanaVarsThis[1]);
}
botanaPolynomialsThis[conicPolysNo] = PPolynomial.collinear(
botanaVarsThis[0], botanaVarsThis[1], vg[0], vg[1],
vg[2], vg[3]);
if (botanaPolynomials == null) {
botanaPolynomials = new HashMap<GeoElementND, PPolynomial[]>();
}
botanaPolynomials.put(geo, botanaPolynomialsThis);
return botanaPolynomialsThis;
}
throw new NoSymbolicParametersException();
} else {
throw new NoSymbolicParametersException();
}
}
/**
* The number of intersections not generated by this algo.
*
* @return previously existing number of intersections
*/
public int existingIntersections() {
if (existingIntersection != null) {
return 1;
}
return 0;
}
}