/* 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.arithmetic; import java.util.HashSet; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.arithmetic.ExpressionNodeConstants.StringType; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoVec2D; import org.geogebra.common.main.MyParseError; /** * * @author Markus */ public class MyVecNode extends ValidExpression implements VectorValue, MyVecNDNode { /** * x coordinate */ protected ExpressionValue x; /** * y coordinate */ protected ExpressionValue y; private int mode = Kernel.COORD_CARTESIAN; private Kernel kernel; private boolean isCASVector = false; /** * Creates new MyVec2D * * @param kernel * kernel */ public MyVecNode(Kernel kernel) { this.kernel = kernel; } /** * Creates new MyVec2D with coordinates (x,y) as ExpresssionNodes. Both * nodes must evaluate to NumberValues. * * @param kernel * kernel * @param x * x-coord * @param y * y-coord */ public MyVecNode(Kernel kernel, ExpressionValue x, ExpressionValue y) { this(kernel); setCoords(x, y); } @Override public MyVecNode deepCopy(Kernel kernel1) { MyVecNode ret = new MyVecNode(kernel1, x.deepCopy(kernel1), y.deepCopy(kernel1)); ret.mode = mode; if (isCASVector()) { ret.setCASVector(); } return ret; } @Override public void resolveVariables(EvalInfo info) { x.resolveVariables(info); y.resolveVariables(info); } /** * @return x-coord */ public ExpressionValue getX() { return x; } /** * @return y-coord */ public ExpressionValue getY() { return y; } /** * @param r * radius * @param phi * phase */ public void setPolarCoords(ExpressionValue r, ExpressionValue phi) { setCoords(r, phi); mode = Kernel.COORD_POLAR; } /** * @return true if uses polar coordinates */ public boolean hasPolarCoords() { return mode == Kernel.COORD_POLAR; } private void setCoords(ExpressionValue x, ExpressionValue y) { this.x = x; this.y = y; } /** * @return array of coordinates */ final public double[] getCoords() { StringTemplate tpl = StringTemplate.defaultTemplate; // check if both ExpressionNodes represent NumberValues ExpressionValue evx = x.evaluate(tpl); if (!(evx instanceof NumberValue)) { String[] str = { "NumberExpected", evx.wrap().toString(tpl) }; throw new MyParseError(kernel.getApplication().getLocalization(), str); } ExpressionValue evy = y.evaluate(tpl); if (!(evy instanceof NumberValue)) { String[] str = { "NumberExpected", evy.wrap().toString(tpl) }; throw new MyParseError(kernel.getApplication().getLocalization(), str); } if (mode == Kernel.COORD_POLAR) { double r = ((NumberValue) evx).getDouble(); // allow negative radius for US double phi = ((NumberValue) evy).getDouble(); double[] ret = { r * Math.cos(phi), r * Math.sin(phi) }; return ret; } // CARTESIAN double[] ret = { ((NumberValue) evx).getDouble(), ((NumberValue) evy).getDouble() }; return ret; } @Override 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: if (mode == Kernel.COORD_POLAR) { sb.append("point(("); sb.append(print(x, values, tpl)); sb.append(")*cos("); sb.append(print(y, values, tpl)); sb.append("),("); sb.append(print(x, values, tpl)); sb.append(")*sin("); sb.append(print(y, values, tpl)); sb.append("))"); } else { sb.append(isCASVector() ? "ggbvect[" : "point("); printReGiac(sb, x, values, tpl); sb.append(","); printReGiac(sb, y, values, tpl); sb.append(isCASVector() ? "]" : ")"); } break; default: // continue below if (isCASVector && tpl.getStringType().equals(StringType.LATEX)) { sb.append(" \\binom{"); sb.append(print(x, values, tpl)); sb.append("}{"); sb.append(print(y, values, tpl)); sb.append("}"); } else { sb.append(tpl.leftBracket()); sb.append(print(x, values, tpl)); if (mode == Kernel.COORD_CARTESIAN) { sb.append(", "); } else { sb.append("; "); } sb.append(print(y, values, tpl)); sb.append(tpl.rightBracket()); } break; } return sb.toString(); } private static void printReGiac(StringBuilder sb, ExpressionValue x2, boolean values, StringTemplate tpl) { if (x2.unwrap() instanceof Command) { sb.append("re("); } sb.append(print(x2, values, tpl)); if (x2.unwrap() instanceof Command) { sb.append(")"); } } @Override public String toValueString(StringTemplate tpl) { return toString(tpl, true); } @Override public String toLaTeXString(boolean symbolic, StringTemplate tpl) { return toString(tpl, !symbolic); } /** * interface VectorValue implementation */ @Override public GeoVec2D getVector() { GeoVec2D ret = new GeoVec2D(kernel, getCoords()); ret.setMode(mode); return ret; } @Override public boolean isConstant() { return x.isConstant() && y.isConstant(); } @Override public boolean isLeaf() { return true; } /** POLAR or CARTESIAN */ @Override public int getMode() { return mode; } /** 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); } return varset; } @Override public void setMode(int mode) { this.mode = mode; } // could be vector or point @Override public ValueType getValueType() { return this.mode != Kernel.COORD_COMPLEX ? ValueType.NONCOMPLEX2D : ValueType.COMPLEX; } // could be vector or point @Override public boolean evaluatesToVectorNotPoint() { return isCASVector;// this.mode != Kernel.COORD_COMPLEX; } @Override public boolean isNumberValue() { return false; } @Override final public boolean contains(ExpressionValue ev) { return ev == this; } @Override public String toOutputValueString(StringTemplate tpl) { return toValueString(tpl); } /** * @return kernel */ public Kernel getKernel() { return kernel; } @Override public ExpressionValue traverse(Traversing t) { ExpressionValue v = t.process(this); if (v != this) { return v; } x = x.traverse(t); y = y.traverse(t); return this; } @Override public boolean inspect(Inspecting t) { return t.check(this) || x.inspect(t) || y.inspect(t); } @Override public boolean hasCoords() { return true; } /** * 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 2; } @Override public ExpressionValue getUndefinedCopy(Kernel kernel1) { return new GeoVec2D(kernel1, Double.NaN, Double.NaN); } @Override public double[] getPointAsDouble() { return new double[] { x.evaluateDouble(), y.evaluateDouble(), 0 }; } @Override public void replaceChildrenByValues(GeoElement geo) { if (x instanceof ReplaceChildrenByValues) { ((ReplaceChildrenByValues) x).replaceChildrenByValues(geo); } if (y instanceof ReplaceChildrenByValues) { ((ReplaceChildrenByValues) y).replaceChildrenByValues(geo); } } @Override public ExpressionValue evaluate(StringTemplate tpl) { // MyNumberPair used for datafunction -- don't simplify if (!(this instanceof MyNumberPair) && (x.evaluatesToList() || y.evaluatesToList())) { if (x.wrap().containsFreeFunctionVariable(null) || y.wrap().containsFreeFunctionVariable(null)) { return super.evaluate(tpl); } MyList result = new MyList(kernel); ExpressionValue xEval = x.evaluate(tpl); ExpressionValue yEval = 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); } for (int idx = 0; idx < size; idx++) { result.addListElement(new MyVecNode(kernel, MyList.get(xEval, idx), MyList.get(yEval, idx))); } return result; } return super.evaluate(tpl); } }