/* 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. */ /* * GeoVec2D.java * * Created on 31. August 2001, 11:34 */ package org.geogebra.common.kernel.arithmetic3D; import java.util.HashSet; 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.ExpressionNodeConstants.StringType; import org.geogebra.common.kernel.arithmetic.ExpressionValue; import org.geogebra.common.kernel.arithmetic.Inspecting; import org.geogebra.common.kernel.arithmetic.ListValue; import org.geogebra.common.kernel.arithmetic.MyList; import org.geogebra.common.kernel.arithmetic.MyVecNDNode; import org.geogebra.common.kernel.arithmetic.NumberValue; import org.geogebra.common.kernel.arithmetic.ReplaceChildrenByValues; import org.geogebra.common.kernel.arithmetic.Traversing; import org.geogebra.common.kernel.arithmetic.ValidExpression; import org.geogebra.common.kernel.arithmetic.ValueType; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.kernelND.Geo3DVecInterface; import org.geogebra.common.main.MyParseError; import org.geogebra.common.plugin.GeoClass; /** * * @author Markus + ggb3D */ public class MyVec3DNode extends ValidExpression implements Vector3DValue, MyVecNDNode { private ExpressionValue x, y, z; // private int mode = Kernel.COORD_CARTESIAN; private Kernel kernel; private int mode = Kernel.COORD_CARTESIAN_3D; private boolean isCASVector = false; /** * Creates new MyVec3D * * @param kernel * kernel */ public MyVec3DNode(Kernel kernel) { this.kernel = kernel; kernel.getConstruction().addUsedType(GeoClass.POINT3D); } /** * Creates new MyPoint3DNode with coordinates (x,y,z) as ExpresssionNodes. * Both nodes must evaluate to NumberValues. * * @param kernel * kernel * @param x * x coordinate * @param y * y coordinate * @param z * z coordinate */ public MyVec3DNode(Kernel kernel, ExpressionValue x, ExpressionValue y, ExpressionValue z) { this(kernel); setCoords(x, y, z); } @Override public MyVec3DNode deepCopy(Kernel kernel1) { MyVec3DNode ret = new MyVec3DNode(kernel1, x.deepCopy(kernel1), y.deepCopy(kernel1), z.deepCopy(kernel1)); ret.mode = mode; if (isCASVector()) { ret.setCASVector(); } return ret; } @Override public void resolveVariables(EvalInfo info) { x.resolveVariables(info); y.resolveVariables(info); z.resolveVariables(info); } /** * @return x coordinate */ public ExpressionValue getX() { return x; } /** * @return y coordinate */ public ExpressionValue getY() { return y; } /** * @return z coordinate */ public ExpressionValue getZ() { return z; } private void setCoords(ExpressionValue x, ExpressionValue y, ExpressionValue z) { this.x = x; this.y = y; this.z = z; } /** * @return coordinates of this point as array of doubles */ final public double[] getCoords() { // check if both ExpressionNodes represent NumberValues StringTemplate tpl = StringTemplate.defaultTemplate; ExpressionValue evx = x.evaluate(tpl); if (!(evx instanceof NumberValue)) { String[] str = { "NumberExpected", evx.wrap().toString(tpl) }; throw new MyParseError(kernel.getLocalization(), str); } ExpressionValue evy = y.evaluate(tpl); if (!(evy instanceof NumberValue)) { String[] str = { "NumberExpected", evy.wrap().toString(tpl) }; throw new MyParseError(kernel.getLocalization(), str); } ExpressionValue evz = z.evaluate(tpl); if (!(evz instanceof NumberValue)) { String[] str = { "NumberExpected", evz.wrap().toString(tpl) }; throw new MyParseError(kernel.getLocalization(), str); } if (mode == Kernel.COORD_SPHERICAL) { double r = ((NumberValue) evx).getDouble(); // allow negative radius for US double theta = ((NumberValue) evy).getDouble(); double phi = ((NumberValue) evz).getDouble(); double[] ret = { r * Math.cos(theta) * Math.cos(phi), r * Math.sin(theta) * Math.cos(phi), r * Math.sin(phi) }; return ret; } // CARTESIAN 3D double[] ret = { ((NumberValue) evx).getDouble(), ((NumberValue) evy).getDouble(), ((NumberValue) evz).getDouble() }; return ret; } @Override final public String toString(StringTemplate tpl) { return toString(tpl, false); } private String toString(StringTemplate tpl, boolean values) { StringBuilder sb = new StringBuilder(); switch (tpl.getStringType()) { case GIAC: switch (mode) { case Kernel.COORD_SPHERICAL: sb.append("point(("); sb.append(print(x, values, tpl)); sb.append(")*cos("); sb.append(print(y, values, tpl)); sb.append(")*cos("); sb.append(print(z, values, tpl)); sb.append("),("); sb.append(print(x, values, tpl)); sb.append(")*sin("); sb.append(print(y, values, tpl)); sb.append(")*cos("); sb.append(print(z, values, tpl)); sb.append("),("); sb.append(print(x, values, tpl)); sb.append(")*sin("); sb.append(print(z, values, tpl)); sb.append("))"); break; default: case Kernel.COORD_CARTESIAN_3D: sb.append(isCASVector() ? "ggbvect[" : "point("); sb.append(print(x, values, tpl)); sb.append(','); sb.append(print(y, values, tpl)); sb.append(','); sb.append(print(z, values, tpl)); sb.append(isCASVector() ? "]" : ")"); break; } break; default: if (isCASVector && tpl.getStringType().equals(StringType.LATEX)) { sb.append("\\left( \\begin{tabular}{r}"); sb.append(print(x, values, tpl)); sb.append("\\\\"); sb.append(print(y, values, tpl)); sb.append("\\\\ "); sb.append(print(z, values, tpl)); sb.append("\\\\ \\end{tabular} \\right) "); } else { sb.append(tpl.leftBracket()); sb.append(print(x, values, tpl)); appendSeparator(sb); sb.append(print(y, values, tpl)); appendSeparator(sb); sb.append(print(z, values, tpl)); sb.append(tpl.rightBracket()); } } return sb.toString(); } private void appendSeparator(StringBuilder sb) { if (mode == Kernel.COORD_CARTESIAN_3D) { sb.append(", "); } else { sb.append("; "); } } @Override public String toValueString(StringTemplate tpl) { return toString(tpl, true); } @Override final public String toLaTeXString(boolean symbolic, StringTemplate tpl) { return toString(tpl); } /** * interface Point3DValue implementation */ @Override public double[] getPointAsDouble() { // Application.debug("myvec"); return getCoords(); } @Override public boolean isConstant() { return x.isConstant() && y.isConstant() && z.isConstant(); } @Override public boolean isLeaf() { return true; } /** returns all GeoElement objects in the both coordinate subtrees */ @Override public HashSet<GeoElement> getVariables() { HashSet<GeoElement> temp, varset = x.getVariables(); if (varset == null) { varset = new HashSet<GeoElement>(); } temp = y.getVariables(); if (temp != null) { varset.addAll(temp); } temp = z.getVariables(); if (temp != null) { varset.addAll(temp); } return varset; } /** * @return true for 3D point values */ public boolean isPoint3DValue() { return true; } @Override public boolean isNumberValue() { return false; } @Override final public boolean contains(ExpressionValue ev) { return ev == this; } // could be vector or point? @Override public boolean evaluatesToVectorNotPoint() { return isCASVector;// this.mode != Kernel.COORD_COMPLEX; } @Override public Geo3DVecInterface getVector() { double coords[] = getCoords(); Geo3DVecInterface ret = kernel.getManager3D().newGeo3DVec(coords[0], coords[1], coords[2]); ret.setMode(mode); return ret; } @Override public String toOutputValueString(StringTemplate tpl) { return toValueString(tpl); } @Override public ExpressionValue traverse(Traversing t) { ExpressionValue ev = t.process(this); if (ev != this) { return ev; } x = x.traverse(t); y = y.traverse(t); z = z.traverse(t); return this; } @Override public boolean inspect(Inspecting t) { return t.check(this) || x.inspect(t) || y.inspect(t) || z.inspect(t); } /** * @return kernel */ public Kernel getKernel() { return kernel; } @Override public boolean hasCoords() { return true; } /** * Sets the spherical coords and changes the coord mode * * @param r * radius * @param theta * argument * @param phi * alt */ public void setSphericalPolarCoords(ExpressionValue r, ExpressionValue theta, ExpressionValue phi) { setCoords(r, theta, phi); mode = Kernel.COORD_SPHERICAL; } @Override public int getMode() { return mode; } /** * LaTeX form needs to be different in CAS */ public void setCASVector() { isCASVector = true; } @Override public ExpressionNode wrap() { return new ExpressionNode(kernel, this); } @Override public boolean isCASVector() { return isCASVector; } @Override public int getDimension() { return 3; } @Override public ValueType getValueType() { return ValueType.VECTOR3D; } @Override public ExpressionValue getUndefinedCopy(Kernel kernel1) { return kernel1.getManager3D().newGeo3DVec(Double.NaN, Double.NaN, Double.NaN); } @Override public void setMode(int mode) { this.mode = mode; } @Override public void replaceChildrenByValues(GeoElement geo) { if (x instanceof ReplaceChildrenByValues) { ((ReplaceChildrenByValues) x).replaceChildrenByValues(geo); } if (y instanceof ReplaceChildrenByValues) { ((ReplaceChildrenByValues) y).replaceChildrenByValues(geo); } if (z instanceof ReplaceChildrenByValues) { ((ReplaceChildrenByValues) z).replaceChildrenByValues(geo); } } @Override public ExpressionValue evaluate(StringTemplate tpl) { // MyNumberPair used for datafunction -- don't simplify if (x.evaluatesToList() || y.evaluatesToList() || z.evaluatesToList()) { if (x.wrap().containsFreeFunctionVariable(null) || y.wrap().containsFreeFunctionVariable(null) || z.wrap().containsFreeFunctionVariable(null)) { return super.evaluate(tpl); } MyList result = new MyList(kernel); ExpressionValue xEval = x.evaluate(tpl); ExpressionValue yEval = y.evaluate(tpl); ExpressionValue zEval = y.evaluate(tpl); int size = 0; int maxSize = Integer.MAX_VALUE; if (xEval instanceof ListValue) { maxSize = size = ((ListValue) xEval).size(); } if (yEval instanceof ListValue) { size = Math.min(((ListValue) yEval).size(), maxSize); } if (zEval instanceof ListValue) { size = Math.min(((ListValue) zEval).size(), maxSize); } for (int idx = 0; idx < size; idx++) { result.addListElement(new MyVec3DNode(kernel, MyList.get(xEval, idx), MyList.get(yEval, idx), MyList.get(zEval, idx))); } return result; } return super.evaluate(tpl); } }