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.Matrix.Coords;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoPoly;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.geos.GeoVec3D;
import org.geogebra.common.util.debug.Log;
public class AlgoIntersectNpFunctionPolyLine extends AlgoRootNewton {
protected GeoFunction func; // input
protected GeoPoly poly; // input
protected GeoPoint startPoint, rootPoint;
protected boolean polyClosed;
protected int polySegCount, polyPointLength;
private Function diffFunction;
private GeoPoint[] segEndPoints;
private GeoSegment tempSeg;
private double disMinCoordsStart;
/**
* constructor with labels
*
* @param cons
* construction
* @param label
* labels
* @param startPoint
* starting point
* @param func
* GeoFunction(only non polynomials)
* @param poly
* GeoPoly
* @param polyClosed
* indicates whether the poly is polygon(true) or polyLine(false)
*/
AlgoIntersectNpFunctionPolyLine(Construction cons, String[] label,
GeoPoint startPoint, GeoFunction func, GeoPoly poly,
boolean polyClosed) {
this(cons, startPoint, func, poly, polyClosed);
if (label != null) {
rootPoint.setLabel(label[0]);
} else {
rootPoint.setLabel(null);
}
}
/**
* common constructor
*
* @param cons
* construction
* @param func
* GeoFunction(only non polynomials)
* @param poly
* GeoPoly
* @param polyClosed
* indicates whether the poly is polygon(true) or polyLine(false)
*/
AlgoIntersectNpFunctionPolyLine(Construction cons, GeoPoint startPoint,
GeoFunction func, GeoPoly poly, boolean polyClosed) {
super(cons);
this.startPoint = startPoint;
this.func = func;
this.poly = poly;
this.polyClosed = polyClosed;
initElements();
setInputOutput(); // for AlgoElement
setDependencies();
compute();
}
private void initElements() {
// output
rootPoint = new GeoPoint(getConstruction());
tempSeg = new GeoSegment(getConstruction());
segEndPoints = new GeoPoint[2];
for (int i = 0; i < segEndPoints.length; i++) {
segEndPoints[i] = new GeoPoint(getConstruction());
}
polyPointLength = this.poly.getPoints().length;
polySegCount = isPolyClosed() ? polyPointLength : polyPointLength - 1;
diffFunction = new Function(getKernel());
}
@Override
public final void compute() {
Coords minIntersectCoords = null, currentIntersectCoords = null;
if (!(getFunction().isDefined() && getPoly().isDefined()
&& startPoint.isDefined())) {
rootPoint.setUndefined();
Log.debug("either func, poly, or start is not defined");
return;
}
double disCurrCoordsStart = -1.0;
for (int index = 0; index < polySegCount; index++) {
segEndPoints[0] = getPoly().getPoint(index);
segEndPoints[1] = getPoly()
.getPoint((index + 1) % this.polyPointLength);
GeoVec3D.lineThroughPoints(segEndPoints[0], segEndPoints[1],
tempSeg);
tempSeg.setPoints(segEndPoints[0], segEndPoints[1]);
tempSeg.calcLength();
currentIntersectCoords = calcIntersectionPoint(getFunction(),
tempSeg);
if (minIntersectCoords == null) {
if (currentIntersectCoords != null) {
minIntersectCoords = currentIntersectCoords;
disMinCoordsStart = distanceSqr(currentIntersectCoords,
startPoint.getCoords());
}
} else {
if (currentIntersectCoords == null) {
continue;
}
disCurrCoordsStart = distanceSqr(currentIntersectCoords,
startPoint.getCoords());
if (disCurrCoordsStart < disMinCoordsStart) {
minIntersectCoords = currentIntersectCoords;
disMinCoordsStart = disCurrCoordsStart;
}
}
}
if (minIntersectCoords == null) {
rootPoint.setUndefined();
Log.debug("no intersection");
} else {
rootPoint.setCoords(minIntersectCoords, false);
Log.debug("closet intersection found");
}
rootPoint.update();
}
private Coords calcIntersectionPoint(GeoFunction fn, GeoSegment seg) {
Coords temp;
double x;
// check for vertical line a*x + c = 0: intersection at x=-c/a
if (Kernel.isZero(seg.y)) {
x = -seg.z / seg.x;
}
// standard case
else {
// get difference f - line
Function.difference(fn.getFunction(startPoint.inhomX), seg,
diffFunction);
x = calcRoot(diffFunction, startPoint.inhomX);
}
if (Double.isNaN(x)) {
return null;
}
double y = fn.value(x);
temp = new Coords(x, y, 1.0);
// check if the intersection point really is on the line
// this is important for segments and rays
if (!seg.isOnPath(temp, Kernel.MIN_PRECISION)) {
return null;
}
return temp;
}
public GeoPoint getIntersectionPoint() {
return rootPoint;
}
@Override
public Commands getClassName() {
return Commands.Intersect;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_INTERSECT;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[3];
input[0] = this.func;
input[1] = (GeoElement) this.poly;
input[2] = this.startPoint;
super.setOutputLength(1);
super.setOutput(0, rootPoint);
setDependencies();
}
@Override
final public String toString(StringTemplate tpl) {
// Michael Borcherds 2008-03-31
// simplified to allow better translation
return getLoc().getPlain("IntersectionPointOfABWithInitialValueC",
input[0].getLabel(tpl), input[1].getLabel(tpl),
startPoint.getLabel(tpl));
}
public GeoPoly getPoly() {
return this.poly;
}
public boolean isPolyClosed() {
return this.polyClosed;
}
public GeoFunction getFunction() {
return this.func;
}
private static double distanceSqr(Coords A, Coords B) {
double vx = A.getX() - B.getX();
double vy = A.getY() - B.getY();
return vx * vx + vy * vy;
}
}