package org.geogebra.common.kernel.kernelND; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.VarString; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.ExpressionValue; import org.geogebra.common.kernel.arithmetic.FunctionExpander; import org.geogebra.common.kernel.arithmetic.FunctionNVar; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.geos.GeoElement; /** * Abstract class for cartesian curves in any dimension * * @author Mathieu * */ public abstract class GeoSurfaceCartesianND extends GeoElement implements SurfaceEvaluable, VarString { /** coordinates functions */ protected FunctionNVar[] fun; /** derivative functions */ protected FunctionNVar[][] fun1; /** second derivative functions */ protected FunctionNVar[][][] fun2; /** start parameters */ protected double[] startParam; /** end parameters */ protected double[] endParam; /** flag for isDefined() */ protected boolean isDefined = true; private ExpressionNode point; /** * common constructor * * @param c * construction */ public GeoSurfaceCartesianND(Construction c) { super(c); // moved from GeoElement's constructor // must be called from the subclass, see // http://benpryor.com/blog/2008/01/02/dont-call-subclass-methods-from-a-superclass-constructor/ setConstructionDefaults(); // init visual settings } /** * constructor with functions * * @param c * construction * @param point * point expression * @param fun * functions */ public GeoSurfaceCartesianND(Construction c, ExpressionNode point, FunctionNVar[] fun) { this(c); this.fun = fun; this.point = point; } /** * set derivatives (if not already done) */ @Override public void setDerivatives() { if (fun1 != null || fun == null) { return; } // set derivatives FunctionVariable[] vars = fun[0].getFunctionVariables(); fun1 = new FunctionNVar[vars.length][]; for (int j = 0; j < vars.length; j++) { fun1[j] = new FunctionNVar[fun.length]; } if (functionExpander == null) { functionExpander = new FunctionExpander(); } for (int i = 0; i < fun.length; i++) { ExpressionValue ve = fun[i].deepCopy(getKernel()) .traverse(functionExpander); for (int j = 0; j < vars.length; j++) { fun1[j][i] = new FunctionNVar( ve.derivative(vars[j], getKernel()).wrap(), vars); } } } /** * set first and second derivatives (if not already done) */ public void setSecondDerivatives() { if (fun2 != null) { return; } // ensure first derivatives are set setDerivatives(); // set second derivatives FunctionVariable[] vars = fun[0].getFunctionVariables(); fun2 = new FunctionNVar[vars.length][][]; for (int k = 0; k < vars.length; k++) { fun2[k] = new FunctionNVar[vars.length][]; for (int j = 0; j < vars.length; j++) { fun2[k][j] = new FunctionNVar[fun.length]; } if (functionExpander == null) { functionExpander = new FunctionExpander(); } for (int i = 0; i < fun.length; i++) { ExpressionValue ve = fun1[k][i].deepCopy(getKernel()) .traverse(functionExpander); for (int j = 0; j < vars.length; j++) { fun2[k][j][i] = new FunctionNVar( ve.derivative(vars[j], getKernel()).wrap(), vars); // Log.debug(k + "," + j + "," + i + ": " + fun2[k][j][i]); } } } } /** * reset derivatives */ @Override public void resetDerivatives() { fun1 = null; fun2 = null; } private static FunctionExpander functionExpander; /** * Replaces geo and all its dependent geos in this function's expression by * copies of their values. * * @param geo * Element to be replaced */ public void replaceChildrenByValues(GeoElement geo) { for (int i = 0; i < fun.length; i++) { if (fun[i] != null) { fun[i].replaceChildrenByValues(geo); } } } /** * Sets the start and end parameter value of this curve. * * @param startParam * start parameter * @param endParam * end parameter */ public void setIntervals(double[] startParam, double endParam[]) { this.startParam = startParam; this.endParam = endParam; isDefined = true; for (int i = 0; i < startParam.length && isDefined; i++) { isDefined = startParam[i] <= endParam[i]; } } /** * @param i * index of parameter * @return the ith start parameter value for this surface (may be * Double.NEGATIVE_INFINITY) * */ @Override public double getMinParameter(int i) { return startParam[i]; } /** * @param i * index of parameter * @return the largest possible ith parameter value for this surface (may be * Double.POSITIVE_INFINITY) * */ @Override public double getMaxParameter(int i) { return endParam[i]; } /** * returns all class-specific xml tags for getXML */ @Override protected void getXMLtags(StringBuilder sb) { super.getXMLtags(sb); // line thickness and type // getLineStyleXML(sb); } @Override final public boolean isDefined() { return isDefined && fun != null; } /** * @param defined * flag to mark as defined/undefined */ public void setDefined(boolean defined) { isDefined = defined; } @Override public void setUndefined() { isDefined = false; } @Override public String toString(StringTemplate tpl) { StringBuilder sbToString = new StringBuilder(80); sbToString.setLength(0); if (isLabelSet()) { sbToString.append(label); if (fun != null) { sbToString.append('('); sbToString .append(fun[0].getFunctionVariables()[0].toString(tpl)); sbToString.append(','); sbToString .append(fun[0].getFunctionVariables()[1].toString(tpl)); sbToString.append(") = "); } } sbToString.append(toValueString(tpl)); return sbToString.toString(); } @Override public String toValueString(StringTemplate tpl) { if (isDefined()) { StringBuilder sbTemp = new StringBuilder(80); sbTemp.setLength(0); sbTemp.append('('); for (int i = 0; i < fun.length; i++) { sbTemp.append(fun[i].toValueString(tpl)); if (i < fun.length - 1) { sbTemp.append(", "); } } sbTemp.append(')'); return sbTemp.toString(); } return "?"; } /** * @param tpl * string template * @return symbolic string representation */ public String toSymbolicString(StringTemplate tpl) { if (isDefined()) { StringBuilder sbTemp = new StringBuilder(80); sbTemp.setLength(0); sbTemp.append('('); for (int i = 0; i < fun.length; i++) { sbTemp.append(fun[i].toString(tpl)); if (i < fun.length - 1) { sbTemp.append(", "); } } sbTemp.append(')'); return sbTemp.toString(); } return "?"; } @Override public String toLaTeXString(boolean symbolic, StringTemplate tpl) { if (isDefined()) { StringBuilder sbTemp = new StringBuilder(80); if (point == null) { sbTemp.append("\\left(\\begin{array}{c}"); for (int i = 0; i < fun.length; i++) { sbTemp.append(fun[i].toLaTeXString(symbolic, tpl)); if (i < fun.length - 1) { sbTemp.append("\\\\"); } } sbTemp.append("\\end{array}\\right)"); } else { sbTemp.append(point.toLaTeXString(symbolic, tpl)); } return sbTemp.toString(); } return "?"; } @Override public boolean isGeoSurfaceCartesian() { return true; } @Override public boolean isLaTeXDrawableGeo() { return true; } /** * @return point expression if defined as (f(u,v),g(u,v),h(u,v)) */ public ExpressionNode getPointExpression() { return point; } /** * @param u * value of first parameter * @param v * value of second parameter * @return 3D point */ public ExpressionValue evaluateSurface(double u, double v) { // override this in the 3D version return null; } }