package org.geogebra.common.kernel.algos; import java.util.ArrayList; import java.util.List; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.Function; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.arithmetic.IneqTree; import org.geogebra.common.kernel.arithmetic.Inequality; 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.GeoFunctionNVar; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoVec3D; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.util.debug.Log; public class AlgoVertexIneq extends AlgoElement { private OutputHandler<GeoElement> outputPoints; private GeoFunctionNVar p; private List<GeoPoint> vertices; private AlgoElement[][] helpers; private int validVertices; private GeoLine helperLine; private GeoFunction helperFunction; /** * Creates algo for Vertex[poly] (many output points) Creates new unlabeled * vertex algo * * @param cons * construction * @param p * polygon or polyline */ AlgoVertexIneq(Construction cons, GeoFunctionNVar p) { super(cons); this.p = p; vertices = new ArrayList<GeoPoint>(); helperLine = new GeoLine(cons); outputPoints = createOutputPoints(); setInputOutput(); // for AlgoElement compute(); } /** * @param cons * construction * @param labels * labels for output * @param p * inequality */ public AlgoVertexIneq(Construction cons, String[] labels, GeoFunctionNVar p) { this(cons, p); // if only one label (e.g. "A"), new labels will be A_1, A_2, ... setLabels(labels); update(); } private void setLabels(String[] labels) { // if only one label (e.g. "A") for more than one output, new labels // will be A_1, A_2, ... if (labels != null && labels.length == 1 && // outputPoints.size() > 1 && labels[0] != null && !labels[0].equals("")) { outputPoints.setIndexLabels(labels[0]); } else { outputPoints.setLabels(labels); outputPoints.setIndexLabels(outputPoints.getElement(0) .getLabel(StringTemplate.defaultTemplate)); } } @Override protected void setInputOutput() { input = new GeoElement[] { p }; setDependencies(); } @Override public void compute() { validVertices = 0; IneqTree ineqs = p.getIneqs(); int size = ineqs.getSize(); int ai, bi; for (int i = 0; i < size; i++) { for (int j = i + 1; j < size; j++) { Inequality a, b; if (ineqs.get(i).getType().ordinal() < ineqs.get(j).getType() .ordinal()) { ai = i; bi = j; } else { ai = j; bi = i; } a = ineqs.get(ai); b = ineqs.get(bi); typeSwitch(a, b, ai, bi); } } outputPoints.adjustOutputSize(validVertices > 0 ? validVertices : 1); for (int i = 0; i < validVertices; i++) { GeoPointND point = (GeoPointND) outputPoints.getElement(i); point.set(vertices.get(i)); } // other points are undefined for (int i = validVertices; i < outputPoints.size(); i++) { outputPoints.getElement(i).setUndefined(); } } private void typeSwitch(Inequality a, Inequality b, int ai, int bi) { switch (a.getType()) { case INEQUALITY_PARAMETRIC_X: switch (b.getType()) { case INEQUALITY_PARAMETRIC_X: intParamParam(a, b, ai, bi, true); break; case INEQUALITY_PARAMETRIC_Y: intParamXParamY(a, b, ai, bi); break; case INEQUALITY_LINEAR: intParamXLinear(a, b, ai, bi); break; case INEQUALITY_CONIC: intParamConic(a, b, ai, bi, true); break; case INEQUALITY_1VAR_X: intParamOneVar(a, b, ai, bi, true); break; case INEQUALITY_1VAR_Y: intParamXY(a, b); break; case INEQUALITY_IMPLICIT: break; case INEQUALITY_INVALID: break; } break; case INEQUALITY_PARAMETRIC_Y: switch (b.getType()) { case INEQUALITY_PARAMETRIC_Y: intParamParam(a, b, ai, bi, false); break; case INEQUALITY_LINEAR: intParamYLinear(a, b, ai, bi); break; case INEQUALITY_CONIC: intParamConic(a, b, ai, bi, false); break; case INEQUALITY_1VAR_X: intParamYX(a, b); break; case INEQUALITY_1VAR_Y: intParamOneVar(a, b, ai, bi, false); break; default: break; } break; case INEQUALITY_LINEAR: switch (b.getType()) { case INEQUALITY_LINEAR: intLinearLinear(a, b); break; case INEQUALITY_CONIC: intLinearConic(a, b, ai, bi); break; case INEQUALITY_1VAR_X: intLinearX(a, b); break; case INEQUALITY_1VAR_Y: intLinearY(a, b); break; default: break; } break; case INEQUALITY_CONIC: switch (b.getType()) { case INEQUALITY_CONIC: intConicConic(a, b, ai, bi); break; case INEQUALITY_1VAR_X: intConicX(a, b); break; case INEQUALITY_1VAR_Y: intConicY(a, b); default: break; } break; case INEQUALITY_1VAR_X: switch (b.getType()) { case INEQUALITY_1VAR_X: // no intersections possible break; case INEQUALITY_1VAR_Y: intXY(a, b); break; default: break; } break; case INEQUALITY_1VAR_Y: // no intersections possible break; default: Log.debug("Missing case" + a.getType()); } } private void intParamOneVar(Inequality a, Inequality b, int i, int j, boolean transpose) { initHelpers(); if (helpers[i][j] == null) { if (a.getFunBorder().isPolynomialFunction(false)) { setHelper(i, j, kernel.getAlgoDispatcher().getIntersectionAlgorithm( a.getFunBorder(), helperLine)); } else { setHelper(i, j, new AlgoIntersectFunctionLineNewton(cons, a.getFunBorder(), helperLine, new GeoPoint(cons))); } } GeoPoint[] bz = b.getZeros(); for (GeoPoint bp : bz) { helperLine.setCoords(0, 1, -bp.getX()); helpers[i][j].compute(); addVertices(helpers[i][j], transpose, true); } } private void intParamYX(Inequality a, Inequality b) { Log.debug(new Throwable().getStackTrace()[0].getMethodName()); GeoPoint[] bz = b.getZeros(); GeoFunction af = a.getFunBorder(); for (GeoPoint bp : bz) { ensurePoint(); vertices.get(validVertices).setCoords(bp.getX(), af.value(bp.getX()), 1); validVertices++; } } private void intParamYLinear(Inequality a, Inequality b, int i, int j) { initHelpers(); if (helpers[i][j] == null) { if (a.getFunBorder().isPolynomialFunction(false)) { setHelper(i, j, kernel.getAlgoDispatcher().getIntersectionAlgorithm( a.getFunBorder(), b.getLineBorder())); } else { setHelper(i, j, new AlgoIntersectFunctionLineNewton(cons, a.getFunBorder(), b.getLineBorder(), new GeoPoint(cons))); } } else { helpers[i][j].compute(); } addVertices(helpers[i][j], false); } private void intParamParam(Inequality a, Inequality b, int i, int j, boolean transpose) { initHelpers(); if (helpers[i][j] == null) { if (a.getFunBorder().isPolynomialFunction(false)) { setHelper(i, j, new AlgoIntersectPolynomials(cons, a.getFunBorder(), b.getFunBorder())); } else { setHelper(i, j, new AlgoIntersectFunctionsNewton(cons, a.getFunBorder(), b.getFunBorder(), new GeoPoint(cons))); } } else { helpers[i][j].compute(); } addVertices(helpers[i][j], transpose); } private void intParamXY(Inequality a, Inequality b) { GeoPoint[] bz = b.getZeros(); GeoFunction af = a.getFunBorder(); for (GeoPoint bp : bz) { ensurePoint(); vertices.get(validVertices).setCoords(af.value(bp.getX()), bp.getX(), 1); validVertices++; } } private void intParamConic(Inequality a, Inequality b, int i, int j, boolean transpose) { initHelpers(); double[] mat = b.getConicBorder().getMatrix(); if (transpose) { b.getConicBorder().setMatrix(new double[] { mat[1], mat[0], mat[2], mat[3], mat[5], mat[4] }); } if (helpers[i][j] == null) { if (a.getFunBorder().isPolynomialFunction(false)) { setHelper(i, j, new AlgoIntersectPolynomialConic(cons, a.getFunBorder(), b.getConicBorder())); } else { // TODO } } else { helpers[i][j].compute(); } if (transpose) { b.getConicBorder().setMatrix(mat); } if (helpers[i][j] != null) { addVertices(helpers[i][j], transpose); } } private void intParamXLinear(Inequality a, Inequality b, int i, int j) { initHelpers(); GeoLine bl = b.getLineBorder(); double x = bl.getX(); double y = bl.getY(); bl.setCoords(y, x, bl.getZ()); if (helpers[i][j] == null) { if (a.getFunBorder().isPolynomialFunction(false)) { setHelper(i, j, kernel.getAlgoDispatcher().getIntersectionAlgorithm( a.getFunBorder(), b.getLineBorder())); } else { setHelper(i, j, new AlgoIntersectFunctionLineNewton(cons, a.getFunBorder(), b.getLineBorder(), new GeoPoint(cons))); } } else { helpers[i][j].compute(); } bl.setCoords(x, y, bl.getZ()); addVertices(helpers[i][j], true); } private void intParamXParamY(Inequality a, Inequality b, int i, int j) { initHelpers(); ExpressionNode exp = a.getFunBorder().getFunctionExpression() .getCopy(kernel).wrap(); FunctionVariable aVar = a.getFunBorder().getFunction() .getFunctionVariable(); exp = exp.replace(aVar, b.getFunBorder().getFunctionExpression()) .wrap(); if (helperFunction == null) { helperFunction = new GeoFunction(cons); } helperFunction.setFunction(new Function(exp, b.getFunBorder().getFunction().getFunctionVariable())); helperLine.setCoords(1, -1, 0); if (helpers[i][j] == null) { if (helperFunction.isPolynomialFunction(false)) { setHelper(i, j, kernel.getAlgoDispatcher() .getIntersectionAlgorithm(helperFunction, helperLine)); } else { setHelper(i, j, new AlgoIntersectFunctionLineNewton(cons, helperFunction, helperLine, new GeoPoint(cons))); } } else { helpers[i][j].compute(); } GeoElement[] output = helpers[i][j].getOutput(); for (int k = 0; k < output.length; k++) { GeoPoint pt = (GeoPoint) output[k]; double x = pt.getX() / pt.getZ(); pt.setCoords(x, b.getFunBorder().value(x), 1); if (vertices.size() <= validVertices) { vertices.add(pt); } else { vertices.set(validVertices, pt); } validVertices++; } } private void intXY(Inequality a, Inequality b) { GeoPoint[] az = a.getZeros(); GeoPoint[] bz = b.getZeros(); for (GeoPoint ap : az) { for (GeoPoint bp : bz) { ensurePoint(); vertices.get(validVertices).setCoords(ap.getX(), bp.getX(), 1); validVertices++; Log.debug(ap + "," + bp); } } } private double[] co = new double[3]; private void intConicY(Inequality a, Inequality b) { GeoPoint[] bz = b.getZeros(); double[] coef = a.getConicBorder().getMatrix(); for (GeoPoint bp : bz) { co[2] = coef[0]; co[1] = 2 * coef[3] * bp.getX() + 2 * coef[4]; co[0] = coef[1] * bp.getX() * bp.getX() + 2 * coef[5] * bp.getX() + coef[2]; Log.debug(co[0] + "," + co[1] + "," + co[2]); int n = kernel.getEquationSolver().solveQuadratic(co); for (int k = 0; k < n; k++) { ensurePoint(); vertices.get(validVertices).setCoords(co[k], bp.getX(), 1); validVertices++; } } } private void intConicX(Inequality a, Inequality b) { GeoPoint[] bz = b.getZeros(); double[] coef = a.getConicBorder().getMatrix(); for (GeoPoint bp : bz) { co[2] = coef[1]; co[1] = 2 * coef[3] * bp.getX() + 2 * coef[5]; co[0] = coef[0] * bp.getX() * bp.getX() + 2 * coef[4] * bp.getX() + coef[2]; Log.debug(co[0] + "," + co[1] + "," + co[2]); int n = kernel.getEquationSolver().solveQuadratic(co); for (int k = 0; k < n; k++) { ensurePoint(); vertices.get(validVertices).setCoords(bp.getX(), co[k], 1); validVertices++; } } } private void intConicConic(Inequality a, Inequality b, int i, int j) { initHelpers(); if (helpers[i][j] == null) { setHelper(i, j, new AlgoIntersectConics(cons, a.getConicBorder(), b.getConicBorder())); } else { helpers[i][j].compute(); } addVertices(helpers[i][j], false); } private void intLinearY(Inequality a, Inequality b) { GeoPoint[] bz = b.getZeros(); GeoLine af = a.getLineBorder(); if (Kernel.isZero(af.getX())) { return; } for (GeoPoint bp : bz) { ensurePoint(); vertices.get(validVertices).setCoords( (-af.getY() * bp.getX() - af.getZ()) / af.getX(), bp.getX(), 1); validVertices++; } } private void intLinearX(Inequality a, Inequality b) { GeoPoint[] bz = b.getZeros(); GeoLine af = a.getLineBorder(); if (Kernel.isZero(af.getY())) { return; } for (GeoPoint bp : bz) { ensurePoint(); vertices.get(validVertices).setCoords(bp.getX(), (-af.getX() * bp.getX() - af.getZ()) / af.getY(), 1); validVertices++; } } private void intLinearConic(Inequality a, Inequality b, int i, int j) { initHelpers(); if (helpers[i][j] == null) { setHelper(i, j, new AlgoIntersectLineConic(cons, a.getLineBorder(), b.getConicBorder())); } else { updateHelper(i, j, b.getConicBorder(), a.getLineBorder()); } addVertices(helpers[i][j], false); } private void updateHelper(int i, int j, GeoElement first, GeoElement second) { if (helpers[i][j].getInput(0) != first) { helpers[i][j].getInput(0).set(first); } if (helpers[i][j].getInput(1) != second) { helpers[i][j].getInput(1).set(second); } helpers[i][j].compute(); } private void setHelper(int i, int j, AlgoElement algo) { helpers[i][j] = algo; algo.setProtectedInput(true); algo.remove(); } private void addVertices(AlgoElement algoElement, boolean transpose, boolean copy) { GeoElement[] output = algoElement.getOutput(); for (int k = 0; k < output.length; k++) { GeoPoint pt = copy ? (GeoPoint) output[k].copy() : (GeoPoint) output[k]; if (transpose) { double x = pt.getX() / pt.getZ(); double y = pt.getY() / pt.getZ(); pt.setCoords(y, x, 1); } if (vertices.size() <= validVertices) { vertices.add(pt); } else { vertices.set(validVertices, pt); } validVertices++; } } private void addVertices(AlgoElement algoElement, boolean transpose) { addVertices(algoElement, transpose, false); } private void intLinearLinear(Inequality a, Inequality b) { ensurePoint(); GeoVec3D.cross(a.getLineBorder(), b.getLineBorder(), vertices.get(validVertices)); validVertices++; } private void initHelpers() { int n = p.getIneqs().getSize(); if (helpers == null || helpers.length != n) { helpers = new AlgoElement[n][n]; } } private void ensurePoint() { while (vertices.size() <= validVertices || vertices.get(validVertices) == null) { vertices.add(new GeoPoint(cons)); } } @Override public Commands getClassName() { return Commands.Vertex; } /** * @return resulting vertices */ public GeoElement[] getVertex() { return getOutput(); } private OutputHandler<GeoElement> createOutputPoints() { return new OutputHandler<GeoElement>(new elementFactory<GeoElement>() { @Override public GeoPoint newElement() { GeoPoint pt = new GeoPoint(cons); pt.setCoords(0, 0, 1); pt.setParentAlgorithm(AlgoVertexIneq.this); return pt; } }); } }