/* 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.geos; import java.util.HashSet; import org.apache.commons.math3.complex.Complex; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.ExpressionValue; import org.geogebra.common.kernel.arithmetic.FunctionVariable; import org.geogebra.common.kernel.arithmetic.ListValue; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.arithmetic.MyList; 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.VectorNDValue; import org.geogebra.common.kernel.arithmetic.VectorValue; import org.geogebra.common.kernel.commands.EvalInfo; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoVecInterface; import org.geogebra.common.util.MyMath; import org.geogebra.common.util.debug.Log; import org.geogebra.common.util.lang.Unicode; import org.geogebra.common.util.mathIT.Riemann; /** * * @author Markus */ final public class GeoVec2D extends ValidExpression implements VectorValue, GeoVecInterface { private double x = Double.NaN; private double y = Double.NaN; private int mode; // POLAR or CARTESIAN private Kernel kernel; /** * Creates new GeoVec2D * * @param kernel * kernel */ public GeoVec2D(Kernel kernel) { this.kernel = kernel; } /** * Creates new GeoVec2D with coordinates (x,y) * * @param kernel * kernel * @param x * x-coord * @param y * y-coord */ public GeoVec2D(Kernel kernel, double x, double y) { this(kernel); this.x = x; this.y = y; } /** * Creates new GeoVec2D with coordinates (a[0],a[1]) * * @param kernel * kernel * @param a * coordinates */ public GeoVec2D(Kernel kernel, double[] a) { this(kernel); x = a[0]; y = a[1]; } /** * Copy constructor * * @param v * vector to copy */ public GeoVec2D(GeoVec2D v) { this(v.kernel); x = v.x; y = v.y; mode = v.mode; } /** * @return true for imaginary unit */ public boolean isImaginaryUnit() { return mode == Kernel.COORD_COMPLEX && MyDouble.exactEqual(x, 0) && MyDouble.exactEqual(y, 1); } @Override public GeoVec2D deepCopy(Kernel kernel2) { return new GeoVec2D(this); } @Override public void resolveVariables(EvalInfo info) { // do nothing } /** * Creates new GeoVec2D as vector between Points P and Q * * @param kernel * kernel * @param p * start point * @param q * end point */ public GeoVec2D(Kernel kernel, GeoPoint p, GeoPoint q) { this(kernel); x = q.getX() - p.getX(); y = q.getY() - p.getY(); } /** * @param x * new x-coord */ public void setX(double x) { this.x = x; } /** * @param y * new y-coord */ public void setY(double y) { this.y = y; } /** * @param x * new x-coord * @param y * new y-coord */ public void setCoords(double x, double y) { this.x = x; this.y = y; } /** * @param a * array with coords */ public void setCoords(double[] a) { x = a[0]; y = a[1]; } /** * Copy coords from other vector * * @param v * orher vector */ public void setCoords(GeoVec2D v) { x = v.x; y = v.y; } /** * @param r * radius * @param phi * phase */ public void setPolarCoords(double r, double phi) { x = r * Math.cos(phi); y = r * Math.sin(phi); } /** * @return x-coord */ @Override final public double getX() { return x; } /** * @return y-coord */ @Override final public double getY() { return y; } /** * @return radius (polar) */ final public double getR() { return MyMath.length(x, y); } /** * @return phase (polar) */ final public double getPhi() { return Math.atan2(y, x); } /** * @return coordinates as array */ final public double[] getCoords() { double[] res = { x, y }; return res; } /** * Calculates the eucilidian length of this 2D vector. The result is * sqrt(x^2 + y^2). * * @return length of this vector */ final public double length() { return MyMath.length(x, y); } /** * Changes this vector to a vector with the same direction and orientation, * but length 1. */ final public void makeUnitVector() { double len = this.length(); x = x / len; y = y / len; } /** * Returns a new vector with the same direction and orientation, but length * 1. * * @return unit vector with same direction */ final public GeoVec2D getUnitVector() { double len = this.length(); return new GeoVec2D(kernel, x / len, y / len); } /** * Returns the coordinates of a vector with the same direction and * orientation, but length 1. * * @return normalized coords */ final public double[] getUnitCoords() { double len = this.length(); double[] res = { x / len, y / len }; return res; } /** * Calculates the inner product of this vector and vector v. * * @param v * other vector * @return this * v */ final public double inner(GeoVec2D v) { return x * v.x + y * v.y; } /** * Yields true if the coordinates of this vector are equal to those of * vector v. * * @param v * other vector * @return true if both vectors have equal coords */ final public boolean isEqual(GeoVec2D v) { return Kernel.isEqual(x, v.x) && Kernel.isEqual(y, v.y); } /** * Yields true if this vector and v are linear dependent This is done by * calculating the determinant of this vector an v: this = v <=> det(this, * v) = nullvector. * * @param v * other vector * @return true if this is linear dependent on v */ final public boolean linDep(GeoVec2D v) { // v = l* w <=> det(v, w) = o return Kernel.isZero(det(this, v)); } /** * calculates the determinant of u and v. det(u,v) = u1*v2 - u2*v1 * * @param u * u * @param v * v * @return determinant of {u,v} */ final public static double det(GeoVec2D u, GeoVec2D v) { return u.x * v.y - u.y * v.x; /* * // symmetric operation // det(u,v) = -det(v,u) if (u.objectID < * v.objectID) { return u.x * v.y - u.y * v.x; } else { return -(v.x * * u.y - v.y * u.x); } */ } /** * translate this vector by vector v * * @param v * translation vector */ final public void translate(GeoVec2D v) { x += v.x; y += v.y; } /** * rotate this vector by angle phi * * @param phi * angle */ final public void rotate(double phi) { double cos = Math.cos(phi); double sin = Math.sin(phi); double x0 = x * cos - y * sin; y = x * sin + y * cos; x = x0; } /** * mirror this point at point Q * * @param Q * mirror point */ final public void mirror(Coords Q) { x = 2.0 * Q.getX() - x; y = 2.0 * Q.getY() - y; } /** * mirror transform with angle phi [ cos(phi) sin(phi) ] [ sin(phi) * -cos(phi) ] * * @param phi * parameter */ final public void mirror(double phi) { double cos = Math.cos(phi); double sin = Math.sin(phi); double x0 = x * cos + y * sin; y = x * sin - y * cos; x = x0; } /** * returns this + a * * @param a * addend * @return this + a */ final public GeoVec2D add(GeoVec2D a) { GeoVec2D res = new GeoVec2D(kernel, 0, 0); add(this, a, res); return res; } /** * c = a + b * * @param a * addend * @param b * addend * @param c * result */ final public static void add(GeoVec2D a, GeoVec2D b, GeoVec2D c) { c.x = a.x + b.x; c.y = a.y + b.y; if (a.getMode() == Kernel.COORD_COMPLEX || b.getMode() == Kernel.COORD_COMPLEX) { c.setMode(Kernel.COORD_COMPLEX); } } /** * (xc,yc) = (xa + b , yx) ie complex + real for complex nos or (xc,yc) = * (xa + b , yx + b) for Points/Vectors * * @param a * addend * @param b * addend * @param c * result */ final public static void add(GeoVec2D a, NumberValue b, GeoVec2D c) { if (a.getMode() == Kernel.COORD_COMPLEX) { c.x = a.x + b.getDouble(); c.y = a.y; c.setMode(Kernel.COORD_COMPLEX); } else { c.x = a.x + b.getDouble(); c.y = a.y + b.getDouble(); } } /** * vector + 2D list (to give another vector) * * @param a * addend * @param b * addend * @param c * result */ final public static void add(GeoVec2D a, ListValue b, GeoVec2D c) { MyList list = b.getMyList(); if (list.size() != 2) { c.x = Double.NaN; c.y = Double.NaN; return; } double enX = list.getListElement(0).evaluateDouble(); double enY = list.getListElement(1).evaluateDouble(); if (Double.isNaN(enX) || Double.isNaN(enY)) { c.x = Double.NaN; c.y = Double.NaN; return; } c.x = a.x + enX; c.y = a.y + enY; } /* * vector - 2D list (to give another vector) */ /** * @param a * minuend * @param b * subtrahend * @param c * result * @param reverse * true to compute subtrahend - minuend */ final public static void sub(GeoVec2D a, ListValue b, GeoVec2D c, boolean reverse) { MyList list = b.getMyList(); if (list.size() != 2) { c.x = Double.NaN; c.y = Double.NaN; return; } double enX = list.getListElement(0).evaluateDouble(); double enY = list.getListElement(1).evaluateDouble(); if (reverse) { c.x = a.x - enX; c.y = a.y - enY; } else { c.x = enX - a.x; c.y = enY - a.y; } } /** * (xc,yc) = (b - xa, -yx) ie real - complex or (xc,yc) = (b - xa, b - yx) * for Vectors/Points * * @param b * minuend * @param a * subtrahend * @param c * result */ final public static void sub(NumberValue b, GeoVec2D a, GeoVec2D c) { if (a.getMode() == Kernel.COORD_COMPLEX) { c.x = b.getDouble() - a.x; c.y = -a.y; c.setMode(Kernel.COORD_COMPLEX); } else { c.x = b.getDouble() - a.x; c.y = b.getDouble() - a.y; } } /** * (xc,yc) = (xa - b , yx) ie complex - real or (xc,yc) = (xa - b , yx - b) * for Vectors/Points * * @param a * minuend * @param b * subtrahend * @param c * result */ final public static void sub(GeoVec2D a, NumberValue b, GeoVec2D c) { if (a.getMode() == Kernel.COORD_COMPLEX) { c.x = a.x - b.getDouble(); c.y = a.y; c.setMode(Kernel.COORD_COMPLEX); } else { c.x = a.x - b.getDouble(); c.y = a.y - b.getDouble(); } } /** * returns this - a * * @param a * subtrahend * @return this - subtrahend */ final public GeoVec2D sub(GeoVec2D a) { GeoVec2D res = new GeoVec2D(kernel, 0, 0); sub(this, a, res); return res; } /** * c = a - b * * @param a * minuend * @param b * subtrahend * @param c * result */ final public static void sub(GeoVec2D a, GeoVec2D b, GeoVec2D c) { c.x = a.x - b.x; c.y = a.y - b.y; if (a.getMode() == Kernel.COORD_COMPLEX || b.getMode() == Kernel.COORD_COMPLEX) { c.setMode(Kernel.COORD_COMPLEX); } } /** * Multiplies this vector by b * * @param b * factor * @return reference to self */ final public GeoVec2D mult(double b) { x = b * x; y = b * y; return this; } private GeoVec2D mult(GeoVec2D b) { double x0 = x; double y0 = y; x = x0 * b.x - y0 * b.y; y = x0 * b.y + y0 * b.x; return this; } private static int MAXIT = 100; // Maximum number of iterations allowed. /** * Extend definition of Ei to complex numbers * * @return exponential integral */ public final GeoVec2D ei() { GeoVec2D log = new GeoVec2D(kernel); GeoVec2D.complexLog(this, log); GeoVec2D ret = new GeoVec2D(kernel, MyMath.EULER, 0).add(log).add(this); GeoVec2D add = new GeoVec2D(kernel, x, y); for (int i = 2; i < MAXIT; i++) { add.mult(this); add.mult((i - 1) / (double) i / i); ret = ret.add(add); } return ret; } /** * c = a * b * * @param a * factor * @param b * factor * @param c * result */ final public static void mult(GeoVec2D a, double b, GeoVec2D c) { c.x = a.x * b; c.y = a.y * b; } /** * c = a / b Michael Borcherds 2007-12-09 * * @param a * dividend * @param b * divisor * @param c * result * */ final public static void complexDivide(GeoVec2D a, GeoVec2D b, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.divide(new Complex(b.x, b.y)); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = a / b Michael Borcherds 2008-08-12 * * @param a * dividend * @param b * divisor * @param c * result * */ final public static void complexDivide(NumberValue a, GeoVec2D b, GeoVec2D c) { // NB temporary variables *crucial*: a and c can be the same variable // double x1=a.getDouble(), x2 = b.x, y2 = b.y; // complex division // c.x = (x1 * x2 )/(x2 * x2 + y2 * b.y); // c.y = ( - x1 * y2)/(x2 * x2 + y2 * b.y); Complex out = new Complex(a.getDouble(), 0); out = out.divide(new Complex(b.x, b.y)); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = a * b Michael Borcherds 2007-12-09 * * @param a * factor * @param b * factor * @param c * result */ final public static void complexMultiply(GeoVec2D a, GeoVec2D b, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.multiply(new Complex(b.x, b.y)); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = a ^ b Michael Borcherds 2009-03-10 * * @param a * base * @param b * power * @param c * result */ final public static void complexPower(GeoVec2D a, NumberValue b, GeoVec2D c) { if (a.x == 0 && a.y == 0 && b.getDouble() > 0) { c.x = 0; c.y = 0; } else { Complex out = new Complex(a.x, a.y); out = out.log().multiply(b.getDouble()).exp(); c.x = out.getReal(); c.y = out.getImaginary(); } c.setMode(Kernel.COORD_COMPLEX); } /** * c = sqrt(a) Michael Borcherds 2010-02-07 * * @param a * a * @param c * c */ final public static void complexSqrt(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.sqrt(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = sin(a) * * @param a * a * @param c * c */ final public static void complexSin(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.sin(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = cos(a) * * @param a * a * @param c * c */ final public static void complexCos(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.cos(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = tan(a) * * @param a * a * @param c * c */ final public static void complexTan(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.tan(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = sinh(a) * * @param a * a * @param c * c */ final public static void complexSinh(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.sinh(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = cosh(a) * * @param a * a * @param c * c */ final public static void complexCosh(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.cosh(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = tanh(a) * * @param a * a * @param c * c */ final public static void complexTanh(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.tanh(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = sec(a) * * @param a * a * @param c * c */ final public static void complexSec(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = Complex.ONE.divide(out.cos()); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = csc(a) * * @param a * a * @param c * c */ final public static void complexCsc(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = Complex.ONE.divide(out.sin()); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = cot(a) * * @param a * a * @param c * c */ final public static void complexCot(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = Complex.ONE.divide(out.tan()); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = sech(a) * * @param a * a * @param c * c */ final public static void complexSech(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = Complex.ONE.divide(out.cosh()); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = csc(a) * * @param a * a * @param c * c */ final public static void complexCsch(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = Complex.ONE.divide(out.sinh()); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = cot(a) * * @param a * a * @param c * c */ final public static void complexCoth(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = Complex.ONE.divide(out.tanh()); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = zeta(a) Michael Borcherds 2010-02-07 * * @param a * a * @param c * c */ final public static void complexZeta(GeoVec2D a, GeoVec2D c) { double[] s = { a.x, a.y }; s = Riemann.zeta(s); c.x = s[0]; // real c.y = s[1]; // imaginary c.setMode(Kernel.COORD_COMPLEX); } /** * c = cbrt(a) Michael Borcherds 2010-02-07 * * @param a * a * @param c * c */ final public static void complexCbrt(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.pow(new Complex(1 / 3d, 0)); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = conjugate(a) Michael Borcherds 2010-02-07 * * @param a * a * @param c * c */ final public static void complexConjugate(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.conjugate(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = a ^ b Michael Borcherds 2009-03-10 * * @param a * base * @param b * exponent * @param c * result */ final public static void complexPower(NumberValue a, GeoVec2D b, GeoVec2D c) { Complex out; if (MyDouble.exactEqual(a.getDouble(), Math.E)) { // special case for e^(i theta) // (more accurate) out = new Complex(b.x, b.y); out = out.exp(); } else { out = new Complex(a.getDouble(), 0); out = out.pow(new Complex(b.x, b.y)); } c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = e ^ a Michael Borcherds 2009-03-10 * * @param a * power * @param c * result */ final public static void complexExp(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.exp(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = natural log(a) Michael Borcherds 2009-03-10 * * @param a * a * @param c * logaritmus of a */ final public static void complexLog(GeoVec2D a, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.log(); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = abs(a) Michael Borcherds 2009-03-10 * * @param a * a * @return absolute value of a */ final public static double complexAbs(GeoVec2D a) { Complex out = new Complex(a.x, a.y); return out.abs(); } /** * c = a ^ b Michael Borcherds 2009-03-14 * * @param a * base * @param b * exponent * @param c * result */ final public static void complexPower(GeoVec2D a, GeoVec2D b, GeoVec2D c) { Complex out = new Complex(a.x, a.y); out = out.pow(new Complex(b.x, b.y)); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * c = a * b Michael Borcherds 2007-12-09 * * @param a * factor * @param b * factor * @param c * result */ final public static void complexMultiply(GeoVec2D a, NumberValue b, GeoVec2D c) { // NB temporary variables *crucial*: a and c can be the same variable // double x1=a.x,y1=a.y,x2=b.getDouble(); // do multiply // c.x = (x1 * x2); // c.y = (x2 * y1); Complex out = new Complex(a.x, a.y); out = out.multiply(new Complex(b.getDouble(), 0)); c.x = out.getReal(); c.y = out.getImaginary(); c.setMode(Kernel.COORD_COMPLEX); } /** * see also GeoVec3D#vectorProduct() * * @param a * factor * @param b * factor * @param c * vector product */ final public static void vectorProduct(GeoVecInterface a, GeoVecInterface b, MyDouble c) { c.set(a.getX() * b.getY() - a.getY() * b.getX()); } /** * @param a * factor * @param b * factor * @param c * inner product */ final public static void inner(GeoVec2D a, GeoVec2D b, MyDouble c) { c.set(a.x * b.x + a.y * b.y); } /** * c = a / b * * @param a * vector * @param b * divisor * @param c * result */ final public static void div(GeoVec2D a, double b, GeoVec2D c) { c.x = a.x / b; c.y = a.y / b; } @Override final public String toString(StringTemplate tpl) { if (isImaginaryUnit()) { switch (tpl.getStringType()) { case GIAC: return "i"; default: // case GEOGEBRA: // case GEOGEBRA_XML: // case LATEX: return Unicode.IMAGINARY; } } else if (mode == Kernel.COORD_COMPLEX) { initStringBuilder(); sbToString.setLength(0); sbToString.append(tpl.leftBracket()); sbToString.append(kernel.format(x, tpl)); sbToString.append(" "); kernel.formatSignedCoefficient(y, sbToString, tpl); sbToString.append(tpl.getImaginary()); sbToString.append(tpl.rightBracket()); return sbToString.toString(); } initStringBuilder(); sbToString.setLength(0); if (tpl.hasCASType()) { sbToString.append("point"); } sbToString.append('('); sbToString.append(kernel.format(x, tpl)); sbToString.append(", "); sbToString.append(kernel.format(y, tpl)); sbToString.append(')'); return sbToString.toString(); } private StringBuilder sbToString; private void initStringBuilder() { if (sbToString == null) { sbToString = new StringBuilder(50); } } /** * interface VectorValue implementation Make a copy to make sure eg * imaginary(i*5*x) returns 5*x */ @Override final public GeoVec2D getVector() { return new GeoVec2D(this); } @Override final public boolean isConstant() { return true; } @Override final public boolean isLeaf() { return true; } @Override final public int getMode() { return mode; } @Override final public ExpressionValue evaluate(StringTemplate tpl) { return getVector(); } @Override final public HashSet<GeoElement> getVariables() { return null; } @Override final public void setMode(int mode) { this.mode = mode; } @Override final public String toValueString(StringTemplate tpl) { return toString(tpl); } @Override public String toLaTeXString(boolean symbolic, StringTemplate tpl) { return toString(tpl); } // abstract methods of GeoElement /* * final public GeoElement copy() { return new GeoVec2D(this); } * * final public void set(GeoElement geo) { GeoVec2D v = (GeoVec2D) geo; * this.x = v.x; this.y = v.y; } * * final public boolean isDefined() { return true; } */ @Override final public boolean isNumberValue() { return false; } @Override final public ValueType getValueType() { return this.mode != Kernel.COORD_COMPLEX ? ValueType.NONCOMPLEX2D : ValueType.COMPLEX; } @Override final public boolean evaluatesToVectorNotPoint() { return this.mode != Kernel.COORD_COMPLEX; } @Override final public boolean contains(ExpressionValue ev) { return ev == this; } /** * multiplies 2D vector by a 2x2 matrix * * @param list * 2x2 matrix */ public void multiplyMatrix(MyList list) { if (list.getMatrixCols() != 2 || list.getMatrixRows() != 2) { return; } double a, b, c, d; a = MyList.getCell(list, 0, 0).evaluateDouble(); b = MyList.getCell(list, 1, 0).evaluateDouble(); c = MyList.getCell(list, 0, 1).evaluateDouble(); d = MyList.getCell(list, 1, 1).evaluateDouble(); matrixTransform(a, b, c, d); } /** * ret = list * v * * @param list * matrix (assume 2x2) * @param v * vector * @param ret * list * v */ static public void multiplyMatrix(MyList list, GeoVecInterface v, GeoVec2D ret) { double a, b, c, d; a = MyList.getCell(list, 0, 0).evaluateDouble(); b = MyList.getCell(list, 1, 0).evaluateDouble(); c = MyList.getCell(list, 0, 1).evaluateDouble(); d = MyList.getCell(list, 1, 1).evaluateDouble(); Double x1 = a * v.getX() + b * v.getY(); Double y1 = c * v.getX() + d * v.getY(); ret.x = x1; ret.y = y1; } /** * multiplies 2D vector by a 2x2 matrix * * @param list * 2x2 matrix */ public void multiplyMatrixLeft(MyList list) { if (list.getMatrixCols() != 2 || list.getMatrixRows() != 2) { return; } double a, b, c, d; a = MyList.getCell(list, 0, 0).evaluateDouble(); b = MyList.getCell(list, 1, 0).evaluateDouble(); c = MyList.getCell(list, 0, 1).evaluateDouble(); d = MyList.getCell(list, 1, 1).evaluateDouble(); matrixTransform(a, c, b, d); } /** * (1,2)*{{2,0},{0,3}} Transforms the object using the matrix a00 a01 a10 * a11 * * @param a * a00 * @param b * a01 * @param c * a10 * @param d * a11 */ public void matrixTransform(double a, double b, double c, double d) { Double x1 = a * x + b * y; Double y1 = c * x + d * y; x = x1; y = y1; } /** * multiplies 2D vector by a 3x3 affine matrix a b c d e f g h i * * @param list * 3x3 matrix * @param rt * GeoVec3D (as ExpressionValue) to get homogeneous coords from */ public void multiplyMatrixAffine(MyList list, ExpressionValue rt) { if (list.getMatrixCols() != 3 || list.getMatrixRows() != 3) { return; } double a, b, c, d, e, f, g, h, i, z1, xx = x, yy = y, zz = 1; boolean vector = false; if ((rt instanceof GeoPoint) || (rt instanceof GeoLine)) { GeoVec3D p = (GeoVec3D) rt; // use homogeneous coordinates if available xx = p.x; yy = p.y; zz = p.z; } else if (rt instanceof VectorNDValue) { GeoVecInterface v = ((VectorNDValue) rt).getVector(); xx = v.getX(); yy = v.getY(); // consistent with 3D vectors zz = 0; vector = true; } else if (rt instanceof GeoPointND) { // 3D point GeoPointND p = (GeoPointND) rt; // use inhomogeneous coordinates xx = p.getInhomX(); yy = p.getInhomY(); zz = 1; } else { Log.warn("error in GeoVec2D.multiplyMatrixAffine" + (rt == null ? "null" : rt.getValueType())); } a = MyList.getCell(list, 0, 0).evaluateDouble(); b = MyList.getCell(list, 1, 0).evaluateDouble(); c = MyList.getCell(list, 2, 0).evaluateDouble(); d = MyList.getCell(list, 0, 1).evaluateDouble(); e = MyList.getCell(list, 1, 1).evaluateDouble(); f = MyList.getCell(list, 2, 1).evaluateDouble(); g = MyList.getCell(list, 0, 2).evaluateDouble(); h = MyList.getCell(list, 1, 2).evaluateDouble(); i = MyList.getCell(list, 2, 2).evaluateDouble(); x = a * xx + b * yy + c * zz; y = d * xx + e * yy + f * zz; z1 = g * xx + h * yy + i * zz; if (!vector) { x = x / z1; y = y / z1; } else { if (!Kernel.isZero(z1)) { // for a Vector, if z1!=0 then the answer can't be represented // by a 2D vector // so set undefined // won't happen when 3rd row of matrix is (0,0,1) x = Double.NaN; y = Double.NaN; } } } @Override public String toOutputValueString(StringTemplate tpl) { return toValueString(tpl); } /** * Transforms the object using the matrix a00 a01 a02 a10 a11 a12 a20 a21 * a22 * * @param a00 * a00 * @param a01 * a01 * @param a02 * a02 * @param a10 * a10 * @param a11 * a11 * @param a12 * a12 * @param a20 * a20 * @param a21 * a21 * @param a22 * a22 */ public void matrixTransform(double a00, double a01, double a02, double a10, double a11, double a12, double a20, double a21, double a22) { double xx = x; double yy = y; double zz = 1; double x1 = a00 * xx + a01 * yy + a02 * zz; double y1 = a10 * xx + a11 * yy + a12 * zz; double z1 = a20 * xx + a21 * yy + a22 * zz; x = x1 / z1; y = y1 / z1; } /** * @return kernel */ public Kernel getKernel() { return kernel; } @Override public boolean hasCoords() { return true; } @Override public double getZ() { return 0; } /** * @return (Math.round(x), Math.round(y)) */ public GeoVec2D round() { return new GeoVec2D(kernel, Math.round(x), Math.round(y)); } /** * @return (Math.floor(x), Math.floor(y)) */ public GeoVec2D floor() { return new GeoVec2D(kernel, Math.floor(x), Math.floor(y)); } /** * @return (Math.ceil(x), Math.ceil(y)) */ public GeoVec2D ceil() { return new GeoVec2D(kernel, Math.ceil(x), Math.ceil(y)); } @Override public ExpressionNode wrap() { return new ExpressionNode(kernel, this); } @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, y, 0 }; } @Override public ExpressionValue derivative(FunctionVariable fv, Kernel kernel1) { // eg derivative of i // needed for plotting a(x, y) = abs(x + y i) in 3d return (new MyDouble(kernel1, 0)).wrap(); } }