/*
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.
*/
/*
* GeoPoint.java
*
* The point (x,y) has homogeneous coordinates (x,y,1)
*
* Created on 30. August 2001, 17:39
*/
package org.geogebra.common.kernel.geos;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeSet;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.kernel.AnimationManager;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.FixedPathRegionAlgo;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.Locateable;
import org.geogebra.common.kernel.LocateableList;
import org.geogebra.common.kernel.MatrixTransformable;
import org.geogebra.common.kernel.MyPoint;
import org.geogebra.common.kernel.Path;
import org.geogebra.common.kernel.PathMover;
import org.geogebra.common.kernel.PathNormalizer;
import org.geogebra.common.kernel.PathOrPoint;
import org.geogebra.common.kernel.PathParameter;
import org.geogebra.common.kernel.Region;
import org.geogebra.common.kernel.RegionParameters;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.Matrix.CoordMatrix4x4;
import org.geogebra.common.kernel.Matrix.CoordSys;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.algos.AlgoDependentPoint;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoMacro;
import org.geogebra.common.kernel.algos.AlgoPointOnPath;
import org.geogebra.common.kernel.algos.SymbolicParameters;
import org.geogebra.common.kernel.algos.SymbolicParametersAlgo;
import org.geogebra.common.kernel.algos.SymbolicParametersBotanaAlgo;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.MyVecNode;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.arithmetic.ValidExpression;
import org.geogebra.common.kernel.arithmetic.ValueType;
import org.geogebra.common.kernel.arithmetic.VectorValue;
import org.geogebra.common.kernel.commands.ParametricProcessor;
import org.geogebra.common.kernel.kernelND.GeoConicND;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoCurveCartesianND;
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.AbstractProverReciosMethod;
import org.geogebra.common.kernel.prover.NoSymbolicParametersException;
import org.geogebra.common.kernel.prover.polynomial.PPolynomial;
import org.geogebra.common.kernel.prover.polynomial.PVariable;
import org.geogebra.common.plugin.EuclidianStyleConstants;
import org.geogebra.common.plugin.GeoClass;
import org.geogebra.common.plugin.Operation;
import org.geogebra.common.util.ExtendedBoolean;
import org.geogebra.common.util.MyMath;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
/**
* 2D Point
*
* @author Markus
*/
public class GeoPoint extends GeoVec3D implements VectorValue, PathOrPoint,
MatrixTransformable, ConicMirrorable, GeoPointND,
Transformable, SymbolicParametersAlgo, SymbolicParametersBotanaAlgo {
// don't set point size here as this would overwrite
// setConstructionDefaults()
// in GeoElement constructor
// public int pointSize = EuclidianStyleConstants.DEFAULT_POINT_SIZE;
private int pointSize;
private int pointStyle;
private double animationValue;
private Path path;
private PathParameter pathParameter;
private Region region;
private RegionParameters regionParameters;
/** equals x/z when updated */
private double x2D = 0;
/** equals y/z when updated */
private double y2D = 0;
/** inhomogeneous x-coord */
public double inhomX;
/** inhomogeneous y-coord */
public double inhomY;
private boolean isInfinite, isDefined;
private boolean showUndefinedInAlgebraView = true;
private PVariable variableCoordinate1 = null, variableCoordinate2 = null;
private PVariable[] botanaVars;
// list of Locateables (GeoElements) that this point is start point of
// if this point is removed, the Locateables have to be notified
private LocateableList locateableList;
/**
* create an undefined GeoPoint
*
* @param c
* construction
*/
public GeoPoint(Construction c) {
this(c, false);
}
/**
* @param c
* construction
* @param coordMode
* cartesian / polar / ... ; see Kernel.COORD_* constants
*/
public GeoPoint(Construction c, int coordMode) {
this(c, false, coordMode);
}
/**
* @param c
* construction
* @param isHelper
* if is helper point, then don't set construction defaults, etc.
*/
public GeoPoint(Construction c, boolean isHelper) {
this(c, isHelper, Kernel.COORD_CARTESIAN);
}
/**
* @param c
* construction
* @param isHelper
* if is helper point, then don't set construction defaults, etc.
* @param coordMode
* cartesian / polar / ... ; see Kernel.COORD_* constants
*/
public GeoPoint(Construction c, boolean isHelper, int coordMode) {
super(c);
if (!isHelper) {
setMode(coordMode);
setConstructionDefaults();
setAnimationType(ANIMATION_INCREASING);
}
setUndefined();
}
/**
* Creates new GeoPoint
*
* @param c
* construction
* @param label
* label
* @param x
* homogeneous x-coord
* @param y
* homogeneous y-coord
* @param z
* homogeneous z-coord
*/
public GeoPoint(Construction c, String label, double x, double y,
double z) {
this(c, x, y, z);
setLabel(label);
}
/**
* Creates new GeoPoint
*
* @param c
* construction
* @param x
* homogeneous x-coord
* @param y
* homogeneous y-coord
* @param z
* homogeneous z-coord
*/
public GeoPoint(Construction c, double x, double y, double z) {
super(c, x, y, z); // GeoVec3D constructor
setConstructionDefaults();
setAnimationType(ANIMATION_INCREASING);
this.setIncidenceList(null);
}
/**
* Creates point on path
*
* @param c
* construction
* @param path
* path
*/
public GeoPoint(Construction c, Path path) {
super(c);
setConstructionDefaults();
setAnimationType(ANIMATION_INCREASING);
this.path = path;
}
/**
* Creates point in region
*
* @param c
* construction
* @param region
* region
*/
public GeoPoint(Construction c, Region region) {
super(c);
setConstructionDefaults();
this.region = region;
}
@Override
public void setZero() {
setCoords(0, 0, 1);
}
/**
* Sets path parameter to null
*/
final public void clearPathParameter() {
pathParameter = null;
}
@Override
final public PathParameter getPathParameter() {
if (pathParameter == null) {
pathParameter = new PathParameter(0);
}
return pathParameter;
}
@Override
final public RegionParameters getRegionParameters() {
if (regionParameters == null) {
regionParameters = new RegionParameters();
}
return regionParameters;
}
@Override
public int getRelatedModeID() {
return toStringMode == Kernel.COORD_COMPLEX
? EuclidianConstants.MODE_COMPLEX_NUMBER
: EuclidianConstants.MODE_POINT;
}
@Override
public String getTypeString() {
if (toStringMode == Kernel.COORD_COMPLEX) {
return "ComplexNumber";
}
return "Point";
}
@Override
public GeoClass getGeoClassType() {
return GeoClass.POINT;
}
/**
* Copy constructor
*
* @param point
* point to copy
*/
public GeoPoint(GeoPoint point) {
super(point.cons);
setConstructionDefaults();
set(point);
}
@Override
public void set(GeoElementND geo) {
set(geo, true);
}
@Override
public void set(GeoElementND geo, boolean macroFeedback) {
this.isDefined = geo.isDefined();
if (geo.isGeoPoint()) {
GeoPoint p = (GeoPoint) geo;
if (p.pathParameter != null) {
pathParameter = getPathParameter();
pathParameter.set(p.pathParameter);
}
animationValue = p.animationValue;
setCoords(p.x, p.y, p.z, macroFeedback);
setMode(p.toStringMode); // complex etc
} else if (geo.isGeoVector()) {
GeoVector v = (GeoVector) geo;
setCoords(v.getX(), v.getY(), 1d);
setMode(v.getMode()); // complex etc
} else if (geo.isGeoNumeric()) {
GeoNumeric v = (GeoNumeric) geo;
setCoords(v.getDouble(), 0, 1d);
setMode(Kernel.COORD_COMPLEX);
} else {
Log.error(geo.getGeoClassType() + " invalid as point");
throw new IllegalArgumentException();
}
reuseDefinition(geo);
}
@Override
public GeoPoint copy() {
return new GeoPoint(this);
}
/*
* void initSetLabelVisible() { setLabelVisible(true); }
*/
@Override
public void setPointSize(int i) {
pointSize = i;
}
@Override
public int getPointSize() {
return pointSize;
}
/**
* @author Florian Sonner
* @version 2008-07-17
*/
@Override
final public int getPointStyle() {
return pointStyle;
}
/**
* @author Florian Sonner
* @version 2008-07-17
* @param style
* the new style to use
*/
@Override
public void setPointStyle(int style) {
if (style > -1 && style <= EuclidianStyleConstants.MAX_POINT_STYLE) {
pointStyle = style;
} else {
pointStyle = -1;
}
}
@Override
public boolean isPointerChangeable() {
return isPointChangeable(this);
}
/**
* static method for used in GeoPoint3D
*
* @param point
* point
* @return true if point is Changeable
*/
public static final boolean isPointChangeable(GeoElement point) {
// if we drag a AlgoDynamicCoordinates, we want its point to be dragged
AlgoElement algo = point.getParentAlgorithm();
// make sure Point[circle, param] is not draggable
if (algo instanceof FixedPathRegionAlgo) {
return ((FixedPathRegionAlgo) algo).isChangeable(point)
&& !point.isLocked();
}
return !point.isLocked() && (point.isIndependent()
|| point.isPointOnPath() || point.isPointInRegion());
}
@Override
public boolean moveFromChangeableCoordParentNumbers(Coords rwTransVec,
Coords targetPosition, Coords viewDirection,
ArrayList<GeoElement> updateGeos,
ArrayList<GeoElement> tempMoveObjectList, EuclidianView view) {
Coords endPosition = targetPosition;
if (!hasChangeableCoordParentNumbers()) {
return false;
}
if (endPosition == null) {
endPosition = getInhomCoords().add(rwTransVec);
}
// translate x and y coordinates by changing the parent coords
// accordingly
ArrayList<NumberValue> freeCoordNumbers = getCoordParentNumbers();
NumberValue xvar = freeCoordNumbers.get(0);
NumberValue yvar = freeCoordNumbers.get(1);
// polar coords (r; phi)
if (hasPolarParentNumbers()) {
// radius
double radius = MyMath.length(endPosition.getX(),
endPosition.getY());
if (xvar instanceof GeoNumeric && xvar != yvar) {
((GeoNumeric) xvar).setValue(xvar.getDouble()
- MyMath.length(inhomX, inhomY) + radius);
}
if (yvar instanceof GeoNumeric) {
// angle
double endAngle = Math.atan2(endPosition.getY(),
endPosition.getX());
double oldAngle = Math.atan2(inhomY, inhomX);
double angle = Kernel.convertToAngleValue(
yvar.getDouble() - oldAngle + endAngle);
// angle outsid of slider range
if (((GeoNumeric) yvar).isIntervalMinActive()
&& ((GeoNumeric) yvar).isIntervalMaxActive()
&& (angle < ((GeoNumeric) yvar).getIntervalMin()
|| angle > ((GeoNumeric) yvar)
.getIntervalMax())) {
// use angle value closest to closest border
double minDiff = Math.abs(
(angle - ((GeoNumeric) yvar).getIntervalMin()));
if (minDiff > Math.PI) {
minDiff = Kernel.PI_2 - minDiff;
}
double maxDiff = Math.abs(
(angle - ((GeoNumeric) yvar).getIntervalMax()));
if (maxDiff > Math.PI) {
maxDiff = Kernel.PI_2 - maxDiff;
}
if (minDiff < maxDiff) {
angle = angle - Kernel.PI_2;
} else {
angle = angle + Kernel.PI_2;
}
}
((GeoNumeric) yvar).setValue(angle);
}
}
// cartesian coords (xvar + constant, yvar + constant)
else {
// only change if GeoNumeric
if (xvar instanceof GeoNumeric) {
GeoNumeric num = (GeoNumeric) xvar;
double newXval = xvar.getDouble() - inhomX + endPosition.getX();
if (num.isSlider()) {
newXval = num.restrictToSliderValues(newXval);
}
num.setValue(newXval);
}
if (xvar != yvar && yvar instanceof GeoNumeric) {
GeoNumeric num = (GeoNumeric) yvar;
double newYval = yvar.getDouble() - inhomY + endPosition.getY();
if (num.isSlider()) {
newYval = num.restrictToSliderValues(newYval);
}
num.setValue(newYval);
}
}
if (xvar instanceof GeoNumeric) {
addChangeableCoordParentNumberToUpdateList((GeoNumeric) xvar,
updateGeos, tempMoveObjectList);
}
if (yvar instanceof GeoNumeric) {
addChangeableCoordParentNumberToUpdateList((GeoNumeric) yvar,
updateGeos, tempMoveObjectList);
}
return true;
}
/**
* Returns whether this point has two changeable numbers as coordinates,
* e.g. point A = (a, b) where a and b are free GeoNumeric objects.
*/
@Override
final public boolean hasChangeableCoordParentNumbers() {
if (isLocked()) {
return false;
}
ArrayList<NumberValue> coords = getCoordParentNumbers();
if (coords.size() == 0) {
return false;
}
NumberValue num1 = coords.get(0);
NumberValue num2 = coords.get(1);
if (num1 == null && num2 == null) {
return false;
}
if (num1 instanceof GeoNumeric && num2 instanceof GeoNumeric) {
GeoElement maxObj1 = GeoElement
.as(((GeoNumeric) num1).getIntervalMaxObject());
GeoElement maxObj2 = GeoElement
.as(((GeoNumeric) num2).getIntervalMaxObject());
GeoElement minObj1 = GeoElement
.as(((GeoNumeric) num1).getIntervalMinObject());
GeoElement minObj2 = GeoElement
.as(((GeoNumeric) num2).getIntervalMinObject());
if (maxObj1 != null && maxObj1.isChildOrEqual((GeoElement) num2)) {
return false;
}
if (minObj1 != null && minObj1.isChildOrEqual((GeoElement) num2)) {
return false;
}
if (maxObj2 != null && maxObj2.isChildOrEqual((GeoElement) num1)) {
return false;
}
if (minObj2 != null && minObj2.isChildOrEqual((GeoElement) num1)) {
return false;
}
}
boolean ret = (num1 instanceof GeoNumeric
&& ((GeoNumeric) num1).isPointerChangeable())
|| (num2 instanceof GeoNumeric
&& ((GeoNumeric) num2).isPointerChangeable());
return ret;
}
/**
* Returns an array of GeoNumeric/MyDouble objects that directly control
* this point's coordinates. For point P = (a, b) the array [a, b] is
* returned, for P = (x(A) + c, d + y(A)) the array [c, d] is returned, for
* P = (x(A) + c, y(A)) the array [c, null] is returned.
*
* for (a,1), [GeoNumeric, MyDouble] is returned
*
* @return null if this point is not defined using two GeoNumeric objects
*/
final public ArrayList<NumberValue> getCoordParentNumbers() {
// init changeableCoordNumbers
if (changeableCoordNumbers == null) {
changeableCoordNumbers = new ArrayList<NumberValue>(2);
AlgoElement parentAlgo = getParentAlgorithm();
// dependent point of form P = (a, b)
if (parentAlgo instanceof AlgoDependentPoint) {
AlgoDependentPoint algo = (AlgoDependentPoint) parentAlgo;
ExpressionNode en = algo.getExpression();
// (xExpression, yExpression)
if (en.isLeaf() && en.getLeft() instanceof MyVecNode) {
// (xExpression, yExpression)
MyVecNode vn = (MyVecNode) en.getLeft();
hasPolarParentNumbers = vn.hasPolarCoords();
try {
// try to get free number variables used in coords for
// this point
// don't allow expressions like "a + x(A)" for polar
// coords (r; phi)
ExpressionValue xcoord = vn.getX();
ExpressionValue ycoord = vn.getY();
ParametricProcessor proc = kernel.getAlgebraProcessor()
.getParamProcessor();
NumberValue xNum = proc.getCoordNumber(xcoord);
NumberValue yNum = proc.getCoordNumber(ycoord);
if (xNum instanceof GeoNumeric
&& ((GeoNumeric) xNum).isPointerChangeable()) {
changeableCoordNumbers.add(xNum);
} else {
changeableCoordNumbers.add(null);
}
if (yNum instanceof GeoNumeric
&& ((GeoNumeric) yNum).isPointerChangeable()) {
changeableCoordNumbers.add(yNum);
} else {
changeableCoordNumbers.add(null);
}
} catch (Throwable e) {
changeableCoordNumbers.clear();
e.printStackTrace();
}
}
}
}
return changeableCoordNumbers;
}
private ArrayList<NumberValue> changeableCoordNumbers = null;
private boolean hasPolarParentNumbers = false;
/**
* @return whether getCoordParentNumbers() returns polar variables (r; phi).
*/
private boolean hasPolarParentNumbers() {
return hasPolarParentNumbers;
}
/**
* Returns whether this number can be animated. Only free numbers with min
* and max interval values can be animated (i.e. shown or hidden sliders).
*/
@Override
public boolean isAnimatable() {
return isPointOnPath() && isPointerChangeable();
}
@Override
public boolean isPointOnPath() {
return path != null;
}
@Override
final public Path getPath() {
return path;
}
/**
* @param p
* path restricting this point
*/
@Override
public void setPath(Path p) {
path = p;
// tell conic that this point is on it, that's needed to handle
// reflections
// of conics correctly for path parameter calculation of point P
GeoElement geo = path.toGeoElement();
if (geo.isGeoConic()) {
((GeoConicND) geo).addPointOnConic(this);// GeoConicND
}
}
/**
* Increments path parameter
*
* @param a
* increment
*/
public void addToPathParameter(double a) {
PathParameter parameter = getPathParameter();
parameter.t += a;
// update point relative to path
path.pathChanged(this);
updateCoords();
}
/**
* Returns true if this point's path is a circle or ellipse
*
* public boolean hasAnglePathParameter() { return (path != null) && (path
* instanceof GeoConic) && (((GeoConic)path).isElliptic()); }
*/
@Override
final public boolean isInfinite() {
return isInfinite;
}
@Override
final public boolean isFinite() {
return isDefined && !isInfinite;
}
@Override
final public boolean showInEuclidianView() {
return isDefined && !isInfinite;
}
@Override
public final boolean showInAlgebraView() {
// intersection points
// return (isDefined || showUndefinedInAlgebraView) && !isI;
return (isDefined || showUndefinedInAlgebraView);
}
@Override
public boolean isDefined() {
return isDefined;
}
/*
* Order of instructions is important here because we need to avoid infinite
* loop setUndefined -> setCoords -> pointChangedForRegion -> setUndefined
*/
@Override
public void setUndefined() {
isDefined = false;
super.setUndefined();
}
@Override
public void setCoords2D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
final public void setCoords(double x, double y, double z) {
setCoords(x, y, z, true);
}
/**
* Sets homogeneous coordinates and updates inhomogeneous coordinates
*
* @param x
* x-coord
* @param y
* y-coord
* @param z
* z-coord
* @param macroFeedback
* whether parent macro may be notified
*/
final public void setCoords(double x, double y, double z,
boolean macroFeedback) {
// set coordinates
this.x = x;
this.y = y;
this.z = z;
setDefinition(null);
// update point on path: this may change coords
// so updateCoords() is called afterwards
if (path != null) {
// remember path parameter for undefined case
PathParameter tempParameter = getTempPathparameter();
tempParameter.set(getPathParameter());
path.pointChanged(this);
// make sure animation starts from the correct place
animationValue = PathNormalizer.toNormalizedPathParameter(
getPathParameter().t, path.getMinParameter(),
path.getMaxParameter());
}
// region
else if (hasRegion()) {
region.pointChangedForRegion(this);
} else if (getParentAlgorithm() != null && macroFeedback) {
if (getParentAlgorithm() instanceof AlgoMacro) {
((AlgoMacro) getParentAlgorithm()).setCoords(this, x, y, z);
}
}
// this avoids multiple computations of inhomogeneous coords;
// see for example distance()
updateCoords();
// undefined and on path: remember old path parameter
if (path != null) {
if (!isDefined) {
PathParameter parameter = getPathParameter();
PathParameter tempParameter = getTempPathparameter();
parameter.set(tempParameter);
} else { // store current path parameter (needed e.g. on file
// loading)
PathParameter tempParameter = getTempPathparameter();
tempParameter.set(getPathParameter());
}
}
}
@Override
protected void setUndefinedCoords() {
// set coordinates
this.x = Double.NaN;
this.y = Double.NaN;
this.z = Double.NaN;
inhomX = Double.NaN;
inhomY = Double.NaN;
// undefined and on path: remember old path parameter
if (path != null) {
PathParameter parameter = getPathParameter();
PathParameter tempParameter = getTempPathparameter();
parameter.set(tempParameter);
}
}
@Override
public void setCoords(Coords v, boolean doPathOrRegion) {
if (doPathOrRegion) {
setCoords(v.getX(), v.getY(), v.getLast());
} else {
// set coordinates
this.x = v.getX();
this.y = v.getY();
this.z = v.getLast();
}
updateCoords();
}
private PathParameter tempPathParameter;
private PathParameter getTempPathparameter() {
if (tempPathParameter == null) {
tempPathParameter = new PathParameter();
}
return tempPathParameter;
}
@Override
final public void updateCoords() {
// infinite point
// #5202
if (!Double.isNaN(z) && Kernel.isEpsilon(z, x, y)) {
isInfinite = true;
isDefined = !(Double.isNaN(x) || Double.isNaN(y));
inhomX = Double.NaN;
inhomY = Double.NaN;
}
// finite point
else {
isInfinite = false;
isDefined = !(Double.isNaN(x) || Double.isNaN(y)
|| Double.isNaN(z));
if (isDefined) {
// make sure the z coordinate is always positive
// this is important for the orientation of a line or ray
// computed using two points P, Q with cross(P, Q)
if (z < 0) {
x = -x;
y = -y;
z = -z;
}
// update inhomogeneous coords
if (z == 1.0) {
inhomX = x;
inhomY = y;
} else {
inhomX = x / z;
inhomY = y / z;
}
} else {
inhomX = Double.NaN;
inhomY = Double.NaN;
}
}
}
/**
* @param r
* radius
* @param phi
* phase
*/
final public void setPolarCoords(double r, double phi) {
setCoords(r * Math.cos(phi), r * Math.sin(phi), 1.0d);
}
@Override
final public void setCoords(GeoVec3D v) {
setCoords(v.x, v.y, v.z);
}
/**
* Sets coords to (x,y,1)
*
* @param v
* vector (x,y)
*/
final public void setCoords(GeoVec2D v) {
setCoords(v.getX(), v.getY(), 1.0);
}
/**
* Yields true if the inhomogeneous coordinates of this point are equal to
* those of point P. Infinite points are checked for linear dependency.
*/
// Michael Borcherds 2008-04-30
@Override
final public boolean isEqual(GeoElementND geo) {
return isEqual(geo, Kernel.STANDARD_PRECISION);
}
/**
* Checks whether geo is a point and whether it's same as this with given
* precision
*
* @param geo
* element
* @param eps
* precision
* @return whether the two points are equal with given precision
*/
final public boolean isEqual(GeoElementND geo, double eps) {
if (!geo.isGeoPoint()) {
return false;
}
if (geo.isGeoElement3D()) {
return geo.isEqual(this); // do the 3D test
}
return isEqualPoint2D((GeoPoint) geo);
}
@Override
public boolean isEqualPointND(GeoPointND geo) {
if (geo.isGeoElement3D()) {
return geo.isEqualPointND(this); // do the 3D test
}
return isEqualPoint2D((GeoPoint) geo);
}
private boolean isEqualPoint2D(GeoPoint P) {
if (!(isDefined() && P.isDefined())) {
return false;
}
// both finite
if (isFinite() && P.isFinite()) {
return Kernel.isEqual(inhomX, P.inhomX)
&& Kernel.isEqual(inhomY, P.inhomY);
} else if (isInfinite() && P.isInfinite()) {
return linDep(P);
} else {
return false;
}
}
/**
* Writes (x/z, y/z) to res.
*/
@Override
final public void getInhomCoords(double[] res) {
res[0] = inhomX;
res[1] = inhomY;
}
/**
* Gets polar coords of this point
*
* @param res
* array to store results
*/
final public void getPolarCoords(double[] res) {
res[0] = MyMath.length(inhomX, inhomY);
res[1] = Math.atan2(inhomY, inhomX);
}
/**
* @return inhomogeneous X
*/
@Override
final public double getInhomX() {
return inhomX;
}
/**
* @return inhomogeneous Y
*/
@Override
final public double getInhomY() {
return inhomY;
}
/**
* @return inhomogeneous Z (in 3D space)
*/
@Override
final public double getInhomZ() {
return 0;
}
@Override
final public double[] vectorTo(GeoPointND QI) {
GeoPoint Q = (GeoPoint) QI;
return new double[] { Q.getInhomX() - getInhomX(),
Q.getInhomY() - getInhomY(), 0 };
}
@Override
public double distance(GeoPointND P) {
// TODO dimension ?
return getInhomCoordsInD3().distance(P.getInhomCoordsInD3());
}
// euclidian distance between this GeoPoint and P
@Override
final public double distance(GeoPoint P) {
return MyMath.length(P.inhomX - inhomX, P.inhomY - inhomY);
}
/**
*
* @param x2
* x coord
* @param y2
* y coord
* @return distance between this and (x,y)
*/
final public double distance(double x2, double y2) {
return MyMath.length(x2 - inhomX, y2 - inhomY);
}
/**
* Convenience method to tell whether these two points are in the same place
*
* @param P
* first point
* @param Q
* second point
* @return true if they are in the same place
*/
final public static boolean samePosition(GeoPoint P, GeoPoint Q) {
return Kernel.isZero(P.distance(Q));
}
/**
* returns the square distance of this point and P (may return infinty or
* NaN).
*
* @param P
* other point
* @return square distance to other point
*/
final public double distanceSqr(GeoPoint P) {
double vx = P.inhomX - inhomX;
double vy = P.inhomY - inhomY;
return vx * vx + vy * vy;
}
/**
* Returns if three points are collinear.
*
* @param A
* first point
* @param B
* second point
* @param C
* third point
* @return whether the three points A, B and C are collinear.
*/
public static boolean collinear(GeoPoint A, GeoPoint B, GeoPoint C) {
// A, B, C are collinear iff det(ABC) == 0
// calculate the determinante of ABC
// det(ABC) = sum1 - sum2
double sum1 = A.x * B.y * C.z + B.x * C.y * A.z + C.x * A.y * B.z;
double sum2 = A.z * B.y * C.x + B.z * C.y * A.x + C.z * A.y * B.x;
// det(ABC) == 0 <=> sum1 == sum2
// A.z, B.z, C.z could be zero
double eps = Math.max(Kernel.MIN_PRECISION,
Kernel.MIN_PRECISION * A.z * B.z * C.z);
return Kernel.isEqual(sum1, sum2, eps);
}
/**
* Decides if 4 points are lying on the same circle.
*
* @param inputPoint1
* first object
* @param inputPoint2
* second object
* @param inputPoint3
* third object
* @param inputPoint4
* forth object
* @return if the inputs are concyclic.
*/
public static boolean concyclic(GeoPoint inputPoint1, GeoPoint inputPoint2,
GeoPoint inputPoint3, GeoPoint inputPoint4) {
double ax = inputPoint1.getX(), ay = inputPoint1.getY(),
az = inputPoint1.getZ(), bx = inputPoint2.getX(),
by = inputPoint2.getY(), bz = inputPoint2.getZ(),
cx = inputPoint3.getX(), cy = inputPoint3.getY(),
cz = inputPoint3.getZ(), dx = inputPoint4.getX(),
dy = inputPoint4.getY(), dz = inputPoint4.getZ();
// Using Ptolemy's theorem
double ab = cz * dz
* Math.sqrt((bx * az - ax * bz) * (bx * az - ax * bz)
+ (by * az - ay * bz) * (by * az - ay * bz));
double ac = bz * dz
* Math.sqrt((cx * az - ax * cz) * (cx * az - ax * cz)
+ (cy * az - ay * cz) * (cy * az - ay * cz));
double ad = bz * cz
* Math.sqrt((dx * az - ax * dz) * (dx * az - ax * dz)
+ (dy * az - ay * dz) * (dy * az - ay * dz));
double bc = az * dz
* Math.sqrt((cx * bz - bx * cz) * (cx * bz - bx * cz)
+ (cy * bz - by * cz) * (cy * bz - by * cz));
double bd = az * cz
* Math.sqrt((dx * bz - bx * dz) * (dx * bz - bx * dz)
+ (dy * bz - by * dz) * (dy * bz - by * dz));
double cd = az * bz
* Math.sqrt((dx * cz - cx * dz) * (dx * cz - cx * dz)
+ (dy * cz - cy * dz) * (dy * cz - cy * dz));
if (Kernel.isZero((ab * cd + bc * ad - ac * bd) / (az * bz * cz * dz),
Kernel.MIN_PRECISION)
|| Kernel.isZero(
(ab * cd + ac * bd - bc * ad) / (az * bz * cz * dz),
Kernel.MIN_PRECISION)
|| Kernel.isZero(
(bc * ad + ac * bd - ab * cd) / (az * bz * cz * dz),
Kernel.MIN_PRECISION)) {
return true;
}
return false;
/*
*
* double ax2=ax*ax, ay2=ay*ay, az2=az*az, bx2=bx*bx, by2=by*by,
* bz2=bz*bz, cx2=cx*cx, cy2=cy*cy, cz2=cz*cz, dx2=dx*dx, dy2=dy*dy,
* dz2=dz*dz;
*
* double det= ax2*bx*bz*cy*cz*dz2 - ax2*bx*bz*cz2*dy*dz -
* ax2*by*bz*cx*cz*dz2 + ax2*by*bz*cz2*dx*dz + ax2*bz2*cx*cz*dy*dz -
* ax2*bz2*cy*cz*dx*dz - ax*az*bx2*cy*cz*dz2 + ax*az*bx2*cz2*dy*dz -
* ax*az*by2*cy*cz*dz2 + ax*az*by2*cz2*dy*dz + ax*az*by*bz*cx2*dz2 +
* ax*az*by*bz*cy2*dz2 - ax*az*by*bz*cz2*dx2 - ax*az*by*bz*cz2*dy2 -
* ax*az*bz2*cx2*dy*dz - ax*az*bz2*cy2*dy*dz + ax*az*bz2*cy*cz*dx2 +
* ax*az*bz2*cy*cz*dy2 + ay2*bx*bz*cy*cz*dz2 - ay2*bx*bz*cz2*dy*dz -
* ay2*by*bz*cx*cz*dz2 + ay2*by*bz*cz2*dx*dz + ay2*bz2*cx*cz*dy*dz -
* ay2*bz2*cy*cz*dx*dz + ay*az*bx2*cx*cz*dz2 - ay*az*bx2*cz2*dx*dz -
* ay*az*bx*bz*cx2*dz2 - ay*az*bx*bz*cy2*dz2 + ay*az*bx*bz*cz2*dx2 +
* ay*az*bx*bz*cz2*dy2 + ay*az*by2*cx*cz*dz2 - ay*az*by2*cz2*dx*dz +
* ay*az*bz2*cx2*dx*dz - ay*az*bz2*cx*cz*dx2 - ay*az*bz2*cx*cz*dy2 +
* ay*az*bz2*cy2*dx*dz - az2*bx2*cx*cz*dy*dz + az2*bx2*cy*cz*dx*dz +
* az2*bx*bz*cx2*dy*dz + az2*bx*bz*cy2*dy*dz - az2*bx*bz*cy*cz*dx2 -
* az2*bx*bz*cy*cz*dy2 - az2*by2*cx*cz*dy*dz + az2*by2*cy*cz*dx*dz -
* az2*by*bz*cx2*dx*dz + az2*by*bz*cx*cz*dx2 + az2*by*bz*cx*cz*dy2 -
* az2*by*bz*cy2*dx*dz; // There may be awful numerical errors
* introduced, so switching to // minimal precision for the current
* calculation (and then back): double precision = Kernel.getEpsilon();
* Kernel.setMinPrecision();
* //outputBoolean.setValue(Kernel.isZero(det));
* AbstractApplication.debug(det); Kernel.setEpsilon(precision);
*/
}
/**
* @param A
* first point
* @param B
* second point
* @param C
* third point
* @return whether the three points A, B and C are collinear.
*/
public static boolean collinearND(GeoPointND A, GeoPointND B,
GeoPointND C) {
// A, B, C are collinear iff (A-B)x(A-C) == (0,0,0)
Coords diffB = A.getInhomCoordsInD3().sub(B.getInhomCoordsInD3());
Coords diffC = A.getInhomCoordsInD3().sub(C.getInhomCoordsInD3());
return !diffB.isLinearIndependent(diffC);
}
/**
* Calcs determinant of P and Q. Note: no test for defined or infinite is
* done here.
*
* @param P
* first point
* @param Q
* second point
* @return determinant
*/
public static final double det(GeoPoint P, GeoPoint Q) {
return (P.x * Q.y - Q.x * P.y) / (P.z * Q.z);
}
/**
* Returns the affine ratio for three collinear points A, B and C. The ratio
* is lambda with C = A + lambda * AB, i.e. lambda = AC/AB. Note: the
* collinearity is not checked in this method.
*
* @param A
* A
* @param B
* B
* @param C
* C
* @return lambda = AC/AB.
*/
public static final double affineRatio(GeoPointND A, GeoPointND B,
GeoPointND C) {
Coords cA = A.getInhomCoordsInD3();
Coords cB = B.getInhomCoordsInD3();
Coords cC = C.getInhomCoordsInD3();
double ABx = cB.getX() - cA.getX();
double ABy = cB.getY() - cA.getY();
double ABz = cB.getZ() - cA.getZ();
// avoid division by a number close to zero
if (Math.abs(ABx) > Math.abs(ABy)) {
if (Math.abs(ABx) > Math.abs(ABz)) {
return (cC.getX() - cA.getX()) / ABx;
}
return (cC.getZ() - cA.getZ()) / ABz;
}
if (Math.abs(ABy) > Math.abs(ABz)) {
return (cC.getY() - cA.getY()) / ABy;
}
return (cC.getZ() - cA.getZ()) / ABz;
}
/***********************************************************
* MOVEMENTS
***********************************************************/
/**
* translate by vector v
*/
@Override
final public void translate(Coords v) {
setCoords(x + v.getX() * z, y + v.getY() * z, z);
}
@Override
final public boolean isTranslateable() {
return true;
}
/**
* dilate from S by r
*/
@Override
final public void dilate(NumberValue rval, Coords S) {
double r = rval.getDouble();
double temp = (1 - r);
setCoords(r * x + temp * S.getX() * z, r * y + temp * S.getY() * z, z);
}
/**
* dilate from O
*
* @param r
* ratio
*/
final public void dilate(double r) {
setCoords(r * x, r * y, z);
}
/**
* rotate this point by angle phi around (0,0)
*/
@Override
final public void rotate(NumberValue phiValue) {
double phi = phiValue.getDouble();
double cos = Math.cos(phi);
double sin = Math.sin(phi);
setCoords(x * cos - y * sin, x * sin + y * cos, z);
}
/**
* rotate this point by angle phi around Q
*/
@Override
final public void rotate(NumberValue phiValue, Coords point) {
Coords Q = point;
double phi = phiValue.getDouble();
double cos = Math.cos(phi);
double sin = Math.sin(phi);
double qx = z * Q.getX();
double qy = z * Q.getY();
setCoords((x - qx) * cos + (qy - y) * sin + qx,
(x - qx) * sin + (y - qy) * cos + qy, z);
}
@Override
final public void rotate(NumberValue phiValue, GeoPointND point) {
rotate(phiValue, point.getInhomCoords());
}
/**
* mirror this point at point Q
*/
@Override
final public void mirror(Coords Q) {
double qx = z * Q.getX();
double qy = z * Q.getY();
setCoords(2.0 * qx - x, 2.0 * qy - y, z);
}
/*
* Michael Borcherds 2008-02-10 Invert point in circle
*/
@Override
final public void mirror(GeoConic c) {
if (c.getType() == GeoConicNDConstants.CONIC_CIRCLE) {
// Mirror point in circle
double r = c.getHalfAxes()[0];
GeoVec2D midpoint = (c.getTranslationVector());
double a = midpoint.getX();
double b = midpoint.getY();
if (Double.isInfinite(x) || Double.isInfinite(y2D)) {
setCoords(a, b, 1.0);
} else {
double sf = r * r / ((inhomX - a) * (inhomX - a)
+ (inhomY - b) * (inhomY - b));
setCoords(a + sf * (inhomX - a), b + sf * (inhomY - b), 1.0);
}
} else if (/*
* c.getType() == GeoConic.CONIC_LINE ||
*/ c.getType() == GeoConicNDConstants.CONIC_PARALLEL_LINES) {
/* In the case the conic is a line we mirror about that line. */
GeoLine g = c.getLines()[0];
/* g = Line[P1,P2] */
mirror(g);
/* g is not needed anymore, so we remove it. */
g.remove();
} else {
setUndefined();
}
}
/**
* mirror this point at line g
*/
@Override
final public void mirror(GeoLineND g1) {
GeoLine g = (GeoLine) g1;
// Y = S(phi).(X - Q) + Q
// where Q is a point on g, S(phi) is the mirrorTransform(phi)
// and phi/2 is the line's slope angle
// get arbitrary point of line
double qx, qy;
if (Math.abs(g.getX()) > Math.abs(g.getY())) {
qx = -z * g.getZ() / g.getX();
qy = 0.0d;
} else {
qx = 0.0d;
qy = -z * g.getZ() / g.getY();
}
// translate -Q
x -= qx;
y -= qy;
// S(phi)
mirrorXY(2.0 * Math.atan2(-g.getX(), g.getY()));
// translate back +Q
x += qx;
y += qy;
// update inhom coords
updateCoords();
}
/***********************************************************/
@Override
final public String toString(StringTemplate tpl) {
sbToString.setLength(0);
sbToString.append(label);
addEqualSignToString(sbToString, toStringMode,
tpl.getCoordStyle(kernel.getCoordStyle()));
sbToString.append(buildValueString(tpl).toString());
return sbToString.toString();
}
/**
* add "=" or not for "A=(...)"
*
* @param sbToString
* string build
* @param toStringMode
* point string mode
* @param coordStyle
* point coord style
*/
static final public void addEqualSignToString(StringBuilder sbToString,
int toStringMode, int coordStyle) {
if (toStringMode == Kernel.COORD_COMPLEX) {
sbToString.append(" = ");
} else {
switch (coordStyle) {
case Kernel.COORD_STYLE_FRENCH:
// no equal sign
sbToString.append(": ");
break;
case Kernel.COORD_STYLE_AUSTRIAN:
// no equal sign
break;
default:
sbToString.append(" = ");
}
}
}
@Override
final public String toStringMinimal(StringTemplate tpl) {
sbToString.setLength(0);
sbToString.append(toValueStringMinimal(tpl));
return sbToString.toString();
}
private StringBuilder sbToString = new StringBuilder(50);
@Override
public String toValueString(StringTemplate tpl) {
return buildValueString(tpl).toString();
}
@Override
final public String toValueStringMinimal(StringTemplate tpl) {
sbBuildValueString.setLength(0);
if (isInfinite()) {
sbBuildValueString.append("?");
return sbBuildValueString.toString();
}
sbBuildValueString.append(regrFormat(inhomX));
sbBuildValueString.append(" ");
sbBuildValueString.append(regrFormat(inhomY));
return sbBuildValueString.toString();
}
private StringBuilder buildValueString(StringTemplate tpl) {
sbBuildValueString.setLength(0);
switch (tpl.getStringType()) {
case GIAC:
if (getDefinition() != null) {
sbBuildValueString.append(getDefinition().toValueString(tpl));
return sbBuildValueString;
}
String xStr = kernel.format(getInhomX(), tpl);
String yStr = kernel.format(getInhomY(), tpl);
if (toStringMode == Kernel.COORD_COMPLEX) {
sbBuildValueString.append("(");
sbBuildValueString.append(xStr);
sbBuildValueString.append("+i*");
sbBuildValueString.append(yStr);
sbBuildValueString.append(")");
} else {
sbBuildValueString.append("point(");
sbBuildValueString.append(xStr);
sbBuildValueString.append(',');
sbBuildValueString.append(yStr);
sbBuildValueString.append(")");
}
return sbBuildValueString;
default: // continue below
}
if (isInfinite()) {
sbBuildValueString.append("?");
return sbBuildValueString;
}
if (getMode() == Kernel.COORD_CARTESIAN_3D) {
buildValueStringCoordCartesian3D(kernel, tpl, getInhomX(),
getInhomY(), 0, sbBuildValueString);
} else if (getMode() == Kernel.COORD_SPHERICAL) {
buildValueStringCoordSpherical(kernel, tpl, getInhomX(),
getInhomY(), 0, sbBuildValueString);
} else {
buildValueString(kernel, tpl, toStringMode, getInhomX(),
getInhomY(), sbBuildValueString);
}
return sbBuildValueString;
}
/**
* @param kernel
* kernel
* @param tpl
* string template
* @param x
* x-coord
* @param y
* y-coord
* @param z
* z-coord
* @param sbBuildValueString
* string builder
*/
public static final void buildValueStringCoordCartesian3D(Kernel kernel,
StringTemplate tpl, double x, double y, double z,
StringBuilder sbBuildValueString) {
if (tpl.hasCASType()) {
sbBuildValueString.append("point(");
sbBuildValueString.append(kernel.format(x, tpl));
sbBuildValueString.append(',');
sbBuildValueString.append(kernel.format(y, tpl));
sbBuildValueString.append(',');
sbBuildValueString.append(kernel.format(z, tpl));
sbBuildValueString.append(")");
return;
}
sbBuildValueString.append('(');
sbBuildValueString.append(kernel.format(x, tpl));
String separator = buildValueStringSeparator(kernel, tpl);
sbBuildValueString.append(separator);
sbBuildValueString.append(" ");
sbBuildValueString.append(kernel.format(y, tpl));
sbBuildValueString.append(separator);
sbBuildValueString.append(" ");
sbBuildValueString.append(kernel.format(z, tpl));
sbBuildValueString.append(')');
}
/**
* @param kernel
* kernel
* @param tpl
* output template
* @return separator for cartesian coords
*/
public static final String buildValueStringSeparator(Kernel kernel,
StringTemplate tpl) {
String separator;
switch (tpl.getCoordStyle(kernel.getCoordStyle())) {
case Kernel.COORD_STYLE_AUSTRIAN:
separator = " |";
break;
default:
separator = Character
.toString(kernel.getLocalization().unicodeComma);
}
if (tpl.hasCASType()) {
separator = ",";
}
return separator;
}
/**
* @param kernel
* kernel
* @param tpl
* string template
* @param x
* x-coord
* @param y
* y-coord
* @param z
* z-coord
* @param sbBuildValueString
* string builder
*/
public static final void buildValueStringCoordSpherical(Kernel kernel,
StringTemplate tpl, double x, double y, double z,
StringBuilder sbBuildValueString) {
double lengthXY = MyMath.length(x, y);
sbBuildValueString.append('(');
sbBuildValueString
.append(kernel.format(MyMath.length(lengthXY, z), tpl));
sbBuildValueString.append("; ");
sbBuildValueString
.append(kernel.formatAngle(Math.atan2(y, x), tpl, false));
sbBuildValueString.append("; ");
sbBuildValueString
.append(kernel.formatAngle(Math.atan2(z, lengthXY), tpl, true));
sbBuildValueString.append(')');
}
/**
* @param kernel
* kernel
* @param tpl
* string template
* @param toStringMode
* Kernel.POLAR, Kernel.CARTESIAN, ...
* @param x
* x-coord
* @param y
* y-coord
* @param sbBuildValueString
* string builder
*/
public static final void buildValueString(Kernel kernel, StringTemplate tpl,
int toStringMode, double x, double y,
StringBuilder sbBuildValueString) {
switch (toStringMode) {
case Kernel.COORD_POLAR:
sbBuildValueString.append('(');
sbBuildValueString.append(kernel.format(MyMath.length(x, y), tpl));
sbBuildValueString.append("; ");
sbBuildValueString
.append(kernel.formatAngle(Math.atan2(y, x), tpl, false));
sbBuildValueString.append(')');
break;
case Kernel.COORD_COMPLEX:
sbBuildValueString.append(kernel.format(x, tpl));
sbBuildValueString.append(" ");
kernel.formatSignedCoefficient(y, sbBuildValueString, tpl);
sbBuildValueString.append(tpl.getImaginary());
break;
default: // CARTESIAN
sbBuildValueString.append('(');
sbBuildValueString.append(kernel.format(x, tpl));
switch (tpl.getCoordStyle(kernel.getCoordStyle())) {
case Kernel.COORD_STYLE_AUSTRIAN:
sbBuildValueString.append(" | ");
break;
default:
sbBuildValueString
.append(kernel.getLocalization().unicodeComma);
sbBuildValueString.append(" ");
}
sbBuildValueString.append(kernel.format(y, tpl));
sbBuildValueString.append(')');
}
}
private StringBuilder sbBuildValueString = new StringBuilder(50);
/**
* interface VectorValue implementation
*/
@Override
public GeoVec2D getVector() {
GeoVec2D ret = new GeoVec2D(kernel, inhomX, inhomY);
ret.setMode(toStringMode);
return ret;
}
/** POLAR or CARTESIAN */
/**
* returns all class-specific xml tags for saveXML GeoGebra File Format
*/
@Override
protected void getXMLtags(StringBuilder sb) {
AlgoElement algo;
if (((algo = getParentAlgorithm()) instanceof AlgoPointOnPath)) {
// write parameter just for GeoCurveCartesian/GeoCurveCartesian3D
// as curve may cross itself so just coords doesn't determine unique
// pos
if (((AlgoPointOnPath) algo)
.getPath() instanceof GeoCurveCartesianND) {
sb.append("\t<curveParam");
sb.append(" t=\"");
sb.append(getPathParameter().t);
sb.append("\"");
sb.append("/>\n");
}
}
// write x,y,z after <curveParam>
super.getXMLtags(sb);
/*
* should not be needed if (path != null) { pathParameter.appendXML(sb);
* }
*/
// polar or cartesian coords
switch (toStringMode) {
case Kernel.COORD_POLAR:
sb.append("\t<coordStyle style=\"polar\"/>\n");
break;
case Kernel.COORD_COMPLEX:
sb.append("\t<coordStyle style=\"complex\"/>\n");
break;
case Kernel.COORD_CARTESIAN_3D:
sb.append("\t<coordStyle style=\"cartesian3d\"/>\n");
break;
case Kernel.COORD_SPHERICAL:
sb.append("\t<coordStyle style=\"spherical\"/>\n");
break;
default:
// don't save default
// sb.append("\t<coordStyle style=\"cartesian\"/>\n");
}
// point size
sb.append("\t<pointSize val=\"");
sb.append(getPointSize());
sb.append("\"/>\n");
// point style
if (pointStyle >= 0) {
sb.append("\t<pointStyle val=\"");
sb.append(pointStyle);
sb.append("\"/>\n");
}
}
@Override
public String getStartPointXML() {
StringBuilder sb = new StringBuilder();
sb.append("\t<startPoint ");
if (isAbsoluteStartPoint()) {
sb.append(" x=\"");
sb.append(x);
sb.append("\"");
sb.append(" y=\"");
sb.append(y);
sb.append("\"");
sb.append(" z=\"");
sb.append(z);
sb.append("\"");
} else {
sb.append("exp=\"");
StringUtil.encodeXML(sb, getLabel(StringTemplate.xmlTemplate));
sb.append("\"");
}
sb.append("/>\n");
return sb.toString();
}
@Override
final public boolean isAbsoluteStartPoint() {
return isIndependent() && !isLabelSet();
}
@Override
public boolean isNumberValue() {
return false;
}
@Override
public boolean evaluatesToNonComplex2DVector() {
return this.getMode() != Kernel.COORD_COMPLEX;
}
@Override
public boolean evaluatesToVectorNotPoint() {
return false;
}
/**
* Calls super.update() and updateCascade() for all registered locateables.
*/
@Override
public void update(boolean drag) {
super.update(drag);
/*
* Log.debug(""); System.out.print("point: " +
* this.getLabel(StringTemplate.defaultTemplate) + " = " +
* this.toString(StringTemplate.defaultTemplate) + "\n" + "il: "); if
* (this.incidenceList!=null) { for (int i=0;
* i<this.incidenceList.size(); i++) {
* System.out.print(incidenceList.get
* (i).getLabel(StringTemplate.defaultTemplate) + " = " +
* incidenceList.get(i).toString(StringTemplate.defaultTemplate) + " ");
* }} System.out.println();
*/
// update all registered locatables (they have this point as start
// point)
if (locateableList != null) {
GeoElement.updateCascadeLocation(locateableList, cons);
}
}
private static volatile TreeSet<AlgoElement> tempSet;
protected static TreeSet<AlgoElement> getTempSet() {
if (tempSet == null) {
tempSet = new TreeSet<AlgoElement>();
}
return tempSet;
}
@Override
public LocateableList getLocateableList() {
if (locateableList == null) {
locateableList = new LocateableList(this);
}
return locateableList;
}
/*
* /** Tells this point that the given Locateable has this point as start
* point.
*
* public void registerLocateable(Locateable l) { if (locateableList ==
* null) locateableList = new ArrayList(); if (locateableList.contains(l))
* return;
*
* // add only locateables that are not already // part of the updateSet of
* this point AlgoElement parentAlgo =
* l.toGeoElement().getParentAlgorithm(); if (parentAlgo == null ||
* !(getAlgoUpdateSet().contains(parentAlgo))) { // add the locatable
* locateableList.add(l); } }
*
* /** Tells this point that the given Locatable no longer has this point as
* start point.
*
* public void unregisterLocateable(Locateable l) { if (locateableList !=
* null) { locateableList.remove(l); } }
*/
/**
* Tells Locateables that their start point is removed and calls
* super.remove()
*/
@Override
public void doRemove() {
if (locateableList != null) {
locateableList.doRemove();
/*
* // copy locateableList into array Object [] locs =
* locateableList.toArray(); locateableList.clear();
*
* // tell all locateables for (int i=0; i < locs.length; i++) {
* Locateable loc = (Locateable) locs[i];
* loc.removeStartPoint(this); loc.toGeoElement().updateCascade(); }
*/
}
// TODO: remove this part because the path should be in incidenceList
// already.
if (path != null) {
GeoElement geo = path.toGeoElement();
if (geo.isGeoConic()) {
((GeoConicND) geo).removePointOnConic(this);// GeoConicND
}
}
// TODO: modify this using removeIncidence
if (incidenceList != null) {
incidenceList.remove(this);
for (int i = 0; i < incidenceList.size(); ++i) {
GeoElement geo = incidenceList.get(i);
if (geo.isGeoConic()) {
((GeoConicND) geo).removePointOnConic(this);// GeoConicND
} else if (geo.isGeoLine()) {
((GeoLineND) geo).removePointOnLine(this);
}
}
}
super.doRemove();
}
@Override
public void setVisualStyle(GeoElement geo) {
super.setVisualStyle(geo);
if (geo.isGeoPoint()) {
setPointSize(((GeoPointND) geo).getPointSize());
pointStyle = ((GeoPointND) geo).getPointStyle();
} else if (geo instanceof PointProperties) {
setPointSize(((PointProperties) geo).getPointSize());
setPointStyle(((PointProperties) geo).getPointStyle());
}
}
@Override
final public boolean isGeoPoint() {
return true;
}
@Override
public void showUndefinedInAlgebraView(boolean flag) {
showUndefinedInAlgebraView = flag;
}
/**
* Returns a comparator for GeoPoint objects. (sorts on X coordinate) If
* equal, doesn't return zero (otherwise TreeSet deletes duplicates)
*
* @return comparator for GeoPoint objects.
*/
public static Comparator<GeoPoint> getComparatorX() {
if (comparatorX == null) {
comparatorX = new Comparator<GeoPoint>() {
@Override
public int compare(GeoPoint itemA, GeoPoint itemB) {
double compX = itemA.inhomX - itemB.inhomX;
if (Kernel.isZero(compX)) {
double compY = itemA.inhomY - itemB.inhomY;
// if x-coords equal, sort on y-coords
if (!Kernel.isZero(compY)) {
return compY < 0 ? -1 : +1;
}
// don't return 0 for equal objects, otherwise the
// TreeSet deletes duplicates
return itemA.getConstructionIndex() > itemB
.getConstructionIndex() ? -1 : 1;
}
return compX < 0 ? -1 : +1;
}
};
}
return comparatorX;
}
private static volatile Comparator<GeoPoint> comparatorX;
// ///////////////////////////////////////////
// REGION
@Override
final public boolean isPointInRegion() {
return region != null;
}
@Override
public boolean hasRegion() {
return region != null;
}
@Override
public Region getRegion() {
return region;
}
/**
* @param a_region
* region restricting this point
*/
@Override
public void setRegion(Region a_region) {
region = a_region;
}
@Override
public void updateCoords2D() {
x2D = x / z;
y2D = y / z;
}
@Override
public double getX2D() {
return x2D;
}
@Override
public double getY2D() {
return y2D;
}
// may be used when 2D point is put on 3D path
@Override
public void updateCoordsFrom2D(boolean doPathOrRegion, CoordSys coordsys) {
if (coordsys != null) {
updateCoords2D();
setCoords(coordsys.getPoint(getX2D(), getY2D()), doPathOrRegion);
}
}
@Override
public void updateCoordsFrom2D(boolean doPathOrRegion) {
// 3D only
}
@Override
public Coords getInhomCoords() {
if (inhomCoords2D == null) {
inhomCoords2D = new Coords(new double[] { inhomX, inhomY });
} else {
this.inhomCoords2D.set(1, inhomX);
this.inhomCoords2D.set(2, inhomY);
}
return inhomCoords2D;
}
private Coords coords2D;
private Coords inhomCoords3D, inhomCoords2D;
@Override
public Coords getInhomCoordsInD(int dimension) {
switch (dimension) {
case 2:
return getInhomCoords();
case 3:
return getInhomCoordsInD3();
default:
return null;
}
}
@Override
public Coords getInhomCoordsInD3() {
if (inhomCoords3D == null) {
inhomCoords3D = new Coords(new double[] { inhomX, inhomY, 0, 1 });
} else {
this.inhomCoords3D.set(1, inhomX);
this.inhomCoords3D.set(2, inhomY);
}
return inhomCoords3D;
}
@Override
public Coords getInhomCoordsInD2() {
return getInhomCoords();
}
private CoordMatrix4x4 tmpMatrix4x4;
private Coords tmpCoords;
@Override
public Coords getCoordsInD2IfInPlane(CoordSys coordSys) {
if (setCoords2D(coordSys)) {
return coords2D;
}
return null;
}
@Override
public Coords getCoordsInD2(CoordSys coordSys) {
setCoords2D(coordSys);
return coords2D;
}
private boolean setCoords2D(CoordSys coordSys) {
if (coords2D == null) {
coords2D = new Coords(new double[] { x, y, z });
}
if (coordSys == null || coordSys == CoordSys.Identity3D
|| coordSys == CoordSys.XOY) {
coords2D.set(1, x / z);
coords2D.set(2, y / z);
coords2D.set(3, 1);
} else { // this should happen only when we try to put a 2D point on a
// 3D path (e.g. GeoConic3D)
// matrix for projection
if (tmpMatrix4x4 == null) {
tmpMatrix4x4 = new CoordMatrix4x4();
}
tmpMatrix4x4.set(coordSys.getMatrixOrthonormal());
if (tmpCoords == null) {
tmpCoords = new Coords(4);
}
getCoordsInD3().projectPlaneInPlaneCoords(tmpMatrix4x4, tmpCoords);
double w = tmpCoords.getW();
coords2D.setX(tmpCoords.getX() / w);
coords2D.setY(tmpCoords.getY() / w);
coords2D.setZ(1);
// check if point is included in the plane
return Kernel.isZero(tmpCoords.getZ());
}
return true;
}
@Override
public Coords getCoordsInD2() {
return getCoordsInD2(null);
}
@Override
public Coords getCoordsInD3() {
return new Coords(x, y, 0, z);
}
@Override
public Coords getCoordsInD(int dimension) {
switch (dimension) {
case 2:
return getCoordsInD2();
case 3:
// Application.debug(getLabel()+": "+x+","+y+","+z);
return getCoordsInD3();
default:
return null;
}
}
@Override
public boolean isMatrixTransformable() {
return true;
}
@Override
public void matrixTransform(double a, double b, double c, double d) {
Double x1 = a * x + b * y;
Double y1 = c * x + d * y;
setCoords(x1, y1, z);
}
// ////////////////////////////////////
// 3D stuff
// ////////////////////////////////////
@Override
public boolean hasDrawable3D() {
return true;
}
@Override
public Coords getLabelPosition() {
/*
* Coords v = new Coords(4); v.set(getInhomCoordsInD3()); v.setW(1);
*/
return getInhomCoordsInD3();
}
@Override
public void pointChanged(GeoPointND p) {
pointChanged(p, x, y, z);
}
/**
* do pointChanged for (x,y,z) 2D coords
*
* @param p
* point
* @param x
* x coord
* @param y
* y coord
* @param z
* z inhom coord
*/
public static void pointChanged(GeoPointND p, double x, double y,
double z) {
p.setCoords2D(x, y, z);
p.updateCoordsFrom2D(false, null);
p.getPathParameter().setT(0);
}
@Override
public void pathChanged(GeoPointND PI) {
pointChanged(PI);
}
@Override
public boolean isOnPath(GeoPointND PI, double eps) {
return isEqual(PI);
}
@Override
public double getMinParameter() {
return 0;
}
@Override
public double getMaxParameter() {
return 0;
}
@Override
public boolean isClosedPath() {
return false;
}
@Override
public PathMover createPathMover() {
return null;
}
@Override
public double getAnimationValue() {
return animationValue;
}
@Override
public void setAnimationValue(double val) {
animationValue = val;
}
/**
* @param frameRate
* frames per second
* @param p
* animated point
* @param path
* animation path
* @param parent
* parent list for list animations
* @return whether the value of this number was changed
*/
static public GeoElementND doAnimationStep(double frameRate, GeoPointND p,
Path path, GeoList parent) {
PathParameter pp = p.getPathParameter();
GeoElement geo = (GeoElement) p;
// remember old value of parameter to decide whether update is necessary
double oldValue = pp.t;
// compute animation step based on speed and frame rates
double intervalWidth = 1;
double step = intervalWidth * geo.getAnimationSpeed()
* geo.getAnimationDirection()
/ (AnimationManager.STANDARD_ANIMATION_TIME * frameRate);
// update animation value
if (Double.isNaN(p.getAnimationValue())) {
p.setAnimationValue(oldValue);
}
p.setAnimationValue(p.getAnimationValue() + step);
// make sure we don't get outside our interval
switch (geo.getAnimationType()) {
case GeoElement.ANIMATION_DECREASING:
case GeoElement.ANIMATION_INCREASING:
// jump to other end of slider
if (p.getAnimationValue() > 1) {
p.setAnimationValue(p.getAnimationValue() - intervalWidth);
} else if (p.getAnimationValue() < 0) {
p.setAnimationValue(p.getAnimationValue() + intervalWidth);
}
break;
case GeoElement.ANIMATION_INCREASING_ONCE:
// stop if outside range
if (p.getAnimationValue() > 1) {
p.setAnimationValue(1);
geo.setAnimating(false);
} else if (p.getAnimationValue() < 0) {
p.setAnimationValue(0);
geo.setAnimating(false);
}
break;
case GeoElement.ANIMATION_OSCILLATING:
default:
if (p.getAnimationValue() >= 1) {
p.setAnimationValue(1);
geo.changeAnimationDirection();
} else if (p.getAnimationValue() <= 0) {
p.setAnimationValue(0);
geo.changeAnimationDirection();
}
break;
}
// change slider's value without changing animationValue
pp.t = PathNormalizer.toParentPathParameter(p.getAnimationValue(),
path.getMinParameter(), path.getMaxParameter());
// return whether value of slider has changed
if (pp.t != oldValue) {
path.pathChanged(p);
p.updateCoords();
return p;
}
return null;
}
/**
* Performs the next automatic animation step for this numbers. This changes
* the value but will NOT call update() or updateCascade().
*
* @return whether the value of this number was changed
*/
@Override
public synchronized GeoElementND doAnimationStep(double frameRate,
GeoList parent) {
return doAnimationStep(frameRate, this, path, parent);
}
// ///////////////////////////////////////
// MOVING THE POINT (3D)
// ///////////////////////////////////////
@Override
public void switchMoveMode(int mode) {
// 3D only
}
@Override
public int getMoveMode() {
if (hasChangeableCoordParentNumbers()) {
return MOVE_MODE_XY;
}
if (!isIndependent() || isLocked()) {
return MOVE_MODE_NONE;
} else if (isPointOnPath()) {
return MOVE_MODE_Z;
} else {
return MOVE_MODE_XY;
}
}
@Override
final public boolean isCasEvaluableObject() {
return true;
}
@Override
public void matrixTransform(double a00, double a01, double a02, double a10,
double a11, double a12, double a20, double a21, double a22) {
double x1 = a00 * x + a01 * y + a02 * z;
double y1 = a10 * x + a11 * y + a12 * z;
double z1 = a20 * x + a21 * y + a22 * z;
setCoords(x1, y1, z1);
}
/**
* Remove path restricting this point
*/
@Override
public void removePath() {
path = null;
pathParameter = null;
}
// needed for GeoPointND interface for 3D, do nothing
@Override
public void setCoords(double x, double y, double z, double w) {
// 3D only
}
@Override
public void moveDependencies(GeoElement oldGeo) {
if (oldGeo.isGeoPoint() && ((GeoPointND) oldGeo).hasLocateableList()) {
locateableList = ((GeoPointND) oldGeo).getLocateableList();
for (Locateable locPoint : locateableList) {
GeoPointND[] pts = locPoint.getStartPoints();
for (int i = 0; i < pts.length; i++) {
if (pts[i] == (GeoPoint) oldGeo) {
pts[i] = this;
}
}
locPoint.toGeoElement().updateRepaint();
}
((GeoPointND) oldGeo).setLocateableList(null);
}
}
// for identifying incidence by construction
// case by case.
// currently implemented for
// lines: line by two point, intersect lines, line/conic, point on line
// TODO: parallel line, perpenticular line
private ArrayList<GeoElement> incidenceList;
/**
* @return list of objects incident by construction
*/
@Override
public ArrayList<GeoElement> getIncidenceList() {
return incidenceList;
}
/**
* @param list
* list of objects incident by construction
*/
public void setIncidenceList(ArrayList<GeoElement> list) {
if (list == null) {
incidenceList = new ArrayList<GeoElement>();
} else {
incidenceList = new ArrayList<GeoElement>(list);
}
}
/**
* initialize incidenceList
*/
public void createIncidenceList() {
incidenceList = new ArrayList<GeoElement>();
}
/**
* add geo to incidenceList of this, and also add this to pointsOnConic
* (when geo is a conic) or to pointsOnLine (when geo is a line)
*
* @param geo
* incident object
*/
@Override
public void addIncidence(GeoElement geo, boolean isStartPoint) {
if (incidenceList == null) {
createIncidenceList();
}
if (!incidenceList.contains(geo)) {
incidenceList.add(geo);
}
// GeoConicND, GeoLine, GeoPoint are the three types who have an
// incidence list
if (geo.isGeoConic()) {
((GeoConicND) geo).addPointOnConic(this);// GeoConicND
} else if (geo.isGeoLine() && !isStartPoint) {
((GeoLineND) geo).addPointOnLine(this);
}
}
/**
* @param geo
* incident geo tobe removed
*/
@Override
public final void removeIncidence(GeoElement geo) {
if (incidenceList != null) {
incidenceList.remove(geo);
}
if (geo.isGeoConic()) {
((GeoConicND) geo).removePointOnConic(this);
} else if (geo.isGeoLine()) {
((GeoLineND) geo).removePointOnLine(this);
}
}
@Override
public boolean isRandomizable() {
// if we drag a AlgoDynamicCoordinates, we want its point to be dragged
AlgoElement algo = getParentAlgorithm();
// make sure Point[circle, param] is not draggable
// TODO Check if we really want this
if (algo instanceof FixedPathRegionAlgo) {
return ((FixedPathRegionAlgo) algo).isChangeable(this);
}
return isIndependent() || isPointOnPath() || isPointInRegion();
}
@Override
public void randomizeForProbabilisticChecking() {
setCoords(x + (Math.random() * 2 - 1) * z,
y + (Math.random() * 2 - 1) * z, z);
}
@Override
public void setParentAlgorithm(AlgoElement algorithm) {
super.setParentAlgorithm(algorithm);
if (algorithm != null)
{
setConstructionDefaults(); // set colors to dependent colors
}
}
@Override
public boolean movePoint(Coords rwTransVec, Coords endPosition) {
boolean movedGeo = false;
final GeoPoint point = this;
if (endPosition != null) {
point.setCoords(endPosition.getX(), endPosition.getY(), 1);
movedGeo = true;
}
// translate point
else {
double x1 = point.getInhomX() + rwTransVec.getX();
double y1 = point.getInhomY() + rwTransVec.getY();
// round to decimal fraction, e.g. 2.800000000001 to 2.8
if (Math.abs(rwTransVec.getX()) > Kernel.MIN_PRECISION) {
x1 = Kernel.checkDecimalFraction(x1);
}
if (Math.abs(rwTransVec.getY()) > Kernel.MIN_PRECISION) {
y1 = Kernel.checkDecimalFraction(y1);
}
// set translated point coords
point.setCoords(x1, y1, 1);
movedGeo = true;
}
return movedGeo;
}
/**
* @param x
* homegenous x-coord
*/
public void setX(double x) {
this.x = x;
}
/**
* @param y
* homogeneous y-coord
*/
public void setY(double y) {
this.y = y;
}
/**
* @param z
* homogeneous z-coord
*/
public void setZ(double z) {
this.z = z;
}
// protected GeoList spreadsheetTraceList = null;
// protected String[] spreadsheetColumnHeadings = null;
@Override
public void updateColumnHeadingsForTraceValues() {
resetSpreadsheetColumnHeadings();
spreadsheetColumnHeadings.add(getColumnHeadingText(new ExpressionNode(
kernel, kernel.getAlgebraProcessor().getXBracket(), // "x("
Operation.PLUS,
new ExpressionNode(kernel, getNameGeo(), // Name[this]
Operation.PLUS,
kernel.getAlgebraProcessor().getCloseBracket())))); // ")"
spreadsheetColumnHeadings.add(getColumnHeadingText(new ExpressionNode(
kernel, kernel.getAlgebraProcessor().getYBracket(), // "y("
Operation.PLUS,
new ExpressionNode(kernel, getNameGeo(), // Name[this]
Operation.PLUS,
kernel.getAlgebraProcessor().getCloseBracket())))); // ")"
}
@Override
public TraceModesEnum getTraceModes() {
return TraceModesEnum.SEVERAL_VALUES_OR_COPY;
}
@Override
public String getTraceDialogAsValues() {
String name = getLabelTextOrHTML(false);
StringBuilder sb = new StringBuilder();
sb.append("x(");
sb.append(name);
sb.append("), y(");
sb.append(name);
sb.append(")");
return sb.toString();
}
@Override
public void addToSpreadsheetTraceList(
ArrayList<GeoNumeric> spreadsheetTraceList) {
GeoNumeric xx = new GeoNumeric(cons, inhomX);
spreadsheetTraceList.add(xx);
if (isPolar()) {
GeoAngle yy = new GeoAngle(cons, inhomY);
spreadsheetTraceList.add(yy);
} else {
GeoNumeric yy = new GeoNumeric(cons, inhomY);
spreadsheetTraceList.add(yy);
}
}
@Override
public SymbolicParameters getSymbolicParameters() {
return new SymbolicParameters(this);
}
@Override
public void getFreeVariables(HashSet<PVariable> variables)
throws NoSymbolicParametersException {
// if this is a free point
if (algoParent == null) {
if (variableCoordinate1 == null) {
variableCoordinate1 = new PVariable(this);
}
if (variableCoordinate2 == null) {
variableCoordinate2 = new PVariable(this);
}
variableCoordinate1.setTwin(variableCoordinate2);
variableCoordinate2.setTwin(variableCoordinate1);
variables.add(variableCoordinate1);
variables.add(variableCoordinate2);
return;
}
if (algoParent instanceof SymbolicParametersAlgo) {
((SymbolicParametersAlgo) algoParent).getFreeVariables(variables);
return;
}
throw new NoSymbolicParametersException();
}
@Override
public int[] getDegrees() throws NoSymbolicParametersException {
if (algoParent == null) {
GeoElement[] fixedElements = AbstractProverReciosMethod
.getFixedPoints();
if (fixedElements != null) {
boolean isContained = false;
for (GeoElement ge : fixedElements) {
if (ge.equals(this)) {
isContained = true;
}
}
if (isContained) {
int[] result = { 0, 0, 0 };
return result;
}
}
int[] result = { 1, 1, 0 };
return result;
}
if (algoParent instanceof SymbolicParametersAlgo) {
return ((SymbolicParametersAlgo) algoParent).getDegrees();
}
throw new NoSymbolicParametersException();
}
@Override
public BigInteger[] getExactCoordinates(
final HashMap<PVariable, BigInteger> values)
throws NoSymbolicParametersException {
if (algoParent == null) {
BigInteger[] result = new BigInteger[3];
result[0] = values.get(variableCoordinate1);
result[1] = values.get(variableCoordinate2);
result[2] = BigInteger.ONE;
if (result[0] == null || result[1] == null) {
throw new NoSymbolicParametersException();
}
return result;
}
if (algoParent instanceof SymbolicParametersAlgo) {
return ((SymbolicParametersAlgo) algoParent)
.getExactCoordinates(values);
}
throw new NoSymbolicParametersException();
}
@Override
public PPolynomial[] getPolynomials() throws NoSymbolicParametersException {
// if this is a free point
if (algoParent == null) {
if (variableCoordinate1 == null) {
variableCoordinate1 = new PVariable(this);
}
if (variableCoordinate2 == null) {
variableCoordinate2 = new PVariable(this);
}
PPolynomial[] ret = { new PPolynomial(variableCoordinate1),
new PPolynomial(variableCoordinate2), new PPolynomial(1) };
return ret;
}
if (algoParent instanceof SymbolicParametersAlgo) {
return ((SymbolicParametersAlgo) algoParent).getPolynomials();
}
throw new NoSymbolicParametersException();
}
@Override
public PVariable[] getBotanaVars(GeoElementND geo)
throws NoSymbolicParametersException {
if (algoParent instanceof SymbolicParametersBotanaAlgo) {
return ((SymbolicParametersBotanaAlgo) algoParent)
.getBotanaVars(this);
}
if (algoParent == null) {
if (botanaVars == null) {
botanaVars = new PVariable[2];
botanaVars[0] = new PVariable(kernel, true);
botanaVars[1] = new PVariable(kernel, true);
Log.trace("Free point " + geo.getLabelSimple() + "("
+ botanaVars[0] + "," + botanaVars[1] + ")");
}
return botanaVars;
}
Log.debug("There is no implementation yet for " + algoParent);
throw new NoSymbolicParametersException();
}
@Override
public PPolynomial[] getBotanaPolynomials(GeoElementND geo)
throws NoSymbolicParametersException {
if (algoParent instanceof SymbolicParametersBotanaAlgo) {
return ((SymbolicParametersBotanaAlgo) algoParent)
.getBotanaPolynomials(this);
}
return null; // Here maybe an exception should be thrown...?
}
@Override
public int getDimension() {
return 2;
}
@Override
public double distanceToPath(PathOrPoint path1) {
return path1.toGeoElement().distance(this);
}
@Override
public boolean hasLocateableList() {
return locateableList != null;
}
@Override
public void setLocateableList(LocateableList locateableList) {
this.locateableList = locateableList;
}
@Override
public void setCoordsFromPoint(GeoPointND point) {
if (point instanceof GeoPoint) {
setCoords((GeoPoint) point);
} else {
setCoords(point.getCoordsInD2(), true);
}
}
@Override
public void set(double param1, double param2, MyPoint leftPoint,
MyPoint rightPoint) {
x = param2 * leftPoint.x + param1 * rightPoint.x;
y = param2 * leftPoint.y + param1 * rightPoint.y;
z = 1.0;
}
@Override
final public HitType getLastHitType() {
return HitType.ON_BOUNDARY;
}
@Override
public ValueType getValueType() {
return getMode() == Kernel.COORD_COMPLEX ? ValueType.COMPLEX
: ValueType.NONCOMPLEX2D;
}
@Override
public ExtendedBoolean isCongruent(GeoElement geo) {
return ExtendedBoolean.newExtendedBoolean(geo.isGeoPoint());
}
@Override
public ValidExpression toValidExpression() {
return getVector();
}
@Override
public double[] getPointAsDouble() {
return new double[] { inhomX, inhomY, 0 };
}
/**
* @param A
* first point
* @param B
* second point
* @param C
* third point
* @param wA
* weight of A
* @param wB
* weight of B
* @param wC
* weight of C
* @param w
* value of wA+wB+wC
* @param M
* output
*/
public static void setBarycentric(GeoPointND A, GeoPointND B, GeoPointND C,
double wA, double wB, double wC, double w, GeoPointND M) {
Coords cA = A.getInhomCoordsInD3();
Coords cB = B.getInhomCoordsInD3();
Coords cC = C.getInhomCoordsInD3();
Coords cM = cA.copy().mulInside(wA / w).addInsideMul(cB, wB / w)
.addInsideMul(cC, wC / w);
M.setCoords(cM, false);
}
@Override
public void setChangeableCoordParentIfNull(ChangeableCoordParent ccp) {
// used for GeoPoint3D
}
@Override
public double getZScale() {
return 1;
}
}