geogebra.kernel.GeoVec2D.java Source code

Java tutorial

Introduction

Here is the source code for geogebra.kernel.GeoVec2D.java

Source

/* 
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 geogebra.kernel;

import geogebra.kernel.arithmetic.ExpressionNode;
import geogebra.kernel.arithmetic.ExpressionValue;
import geogebra.kernel.arithmetic.ListValue;
import geogebra.kernel.arithmetic.MyDouble;
import geogebra.kernel.arithmetic.MyList;
import geogebra.kernel.arithmetic.NumberValue;
import geogebra.kernel.arithmetic.ValidExpression;
import geogebra.kernel.arithmetic.VectorValue;
import geogebra.util.Unicode;

import java.util.HashSet;

import org.apache.commons.math.complex.Complex;

/** 
 * 
 * @author  Markus
 * @version 
 */
final public class GeoVec2D extends ValidExpression implements MatrixTransformable, VectorValue {

    public double x = Double.NaN;
    public double y = Double.NaN;

    private int mode; // POLAR or CARTESIAN  

    private Kernel kernel;

    /** Creates new GeoVec2D */
    public GeoVec2D(Kernel kernel) {
        this.kernel = kernel;
    }

    /** Creates new GeoVec2D with coordinates (x,y)*/
    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])*/
    public GeoVec2D(Kernel kernel, double[] a) {
        this(kernel);
        x = a[0];
        y = a[1];
    }

    /** Copy constructor */
    public GeoVec2D(GeoVec2D v) {
        this(v.kernel);
        x = v.x;
        y = v.y;
        mode = v.mode;
    }

    public boolean isImaginaryUnit() {
        return mode == Kernel.COORD_COMPLEX && x == 0 && y == 1;
    }

    public ExpressionValue deepCopy(Kernel kernel) {
        return new GeoVec2D(this);
    }

    public void resolveVariables() {
    }

    /** Creates new GeoVec2D as vector between Points P and Q */
    public GeoVec2D(Kernel kernel, GeoPoint p, GeoPoint q) {
        this(kernel);
        x = q.x - p.x;
        y = q.y - p.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }

    public void setCoords(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public void setCoords(double[] a) {
        x = a[0];
        y = a[1];
    }

    public void setCoords(GeoVec2D v) {
        x = v.x;
        y = v.y;
    }

    public void setPolarCoords(double r, double phi) {
        x = r * Math.cos(phi);
        y = r * Math.sin(phi);
    }

    final public double getX() {
        return x;
    }

    final public double getY() {
        return y;
    }

    final public double getR() {
        return length(x, y);
    }

    final public double getPhi() {
        return Math.atan2(y, x);
    }

    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).
     */
    final public double length() {
        return length(x, y);
    }

    /** Calculates the eucilidian length of this 2D vector.
     * The result is sqrt(a[0]^2  + a[1]^2).
     */
    final public static double length(double[] a) {
        return length(a[0], a[1]);
    }

    /** Calculates the euclidian length sqrt(a^2  + b^2).     
    */
    final public static double length(double a, double b) {
        //return Math.sqrt(a*a + b*b);

        /*
         * The Algorithm ist taken
         * from Numerical Recipes, Appendix C, p.949 (Cabs) and
         * avoids overflows.         
         */
        double res;
        double x = Math.abs(a);
        double y = Math.abs(b);

        if (x == 0)
            res = y;
        else if (y == 0)
            res = x;
        else if (x > y) {
            double temp = y / x;
            res = x * Math.sqrt(1.0 + temp * temp);
        } else {
            double temp = x / y;
            res = y * Math.sqrt(1.0 + temp * temp);
        }
        return res;

    }

    /** 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.
     */
    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.
     */
    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.
    */
    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. 
     */
    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.
     */
    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
     */
    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
     */
    final public void translate(GeoVec2D v) {
        x += v.x;
        y += v.y;
    }

    /**
     * rotate this vector by angle phi
     */
    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
     */
    final public void mirror(GeoPoint Q) {
        x = 2.0 * Q.inhomX - x;
        y = 2.0 * Q.inhomY - y;
    }

    /**
     * mirror transform with angle phi
     *  [ cos(phi)       sin(phi)   ]
     *  [ sin(phi)      -cos(phi)   ]  
     */
    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 */
    final public GeoVec2D add(GeoVec2D a) {
        GeoVec2D res = new GeoVec2D(kernel, 0, 0);
        add(this, a, res);
        return res;
    }

    /** c = a + b */
    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
     * */
    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) 
     * */
    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;
        }

        ExpressionValue enX = list.getListElement(0).evaluate();
        ExpressionValue enY = list.getListElement(1).evaluate();

        if (!enX.isNumberValue() || !enY.isNumberValue()) {
            c.x = Double.NaN;
            c.y = Double.NaN;
            return;
        }

        c.x = a.x + ((NumberValue) enX).getDouble();
        c.y = a.y + ((NumberValue) enY).getDouble();
    }

    /* vector - 2D list (to give another vector) 
     * */
    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;
        }

        ExpressionValue enX = list.getListElement(0).evaluate();
        ExpressionValue enY = list.getListElement(1).evaluate();

        if (!enX.isNumberValue() || !enY.isNumberValue()) {
            c.x = Double.NaN;
            c.y = Double.NaN;
            return;
        }

        if (reverse) {
            c.x = a.x - ((NumberValue) enX).getDouble();
            c.y = a.y - ((NumberValue) enY).getDouble();
        } else {
            c.x = ((NumberValue) enX).getDouble() - a.x;
            c.y = ((NumberValue) enY).getDouble() - a.y;
        }
    }

    /** (xc,yc) = (b - xa, -yx)  ie real - complex 
     * or (xc,yc) = (b - xa, b - yx)  for Vectors/Points
     * */
    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
     * */
    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 */
    final public GeoVec2D sub(GeoVec2D a) {
        GeoVec2D res = new GeoVec2D(kernel, 0, 0);
        sub(this, a, res);
        return res;
    }

    /** c = a - b */
    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);
    }

    final public void mult(double b) {
        x = b * x;
        y = b * y;
    }

    /** c = a * b */
    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 
     * 
     * */
    final public static void complexDivide(GeoVec2D a, GeoVec2D b, GeoVec2D c) {
        // NB temporary variables *crucial*: a and c can be the same variable
        //double x1=a.x,y1=a.y,x2=b.x,y2=b.y;
        // complex division
        //c.x = (x1 * x2 + y1 * y2)/(x2 * x2 + y2 * b.y);
        //c.y = (y1 * x2 - x1 * y2)/(x2 * x2 + y2 * b.y);

        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 
     * 
     * */
    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 */
    final public static void complexMultiply(GeoVec2D a, GeoVec2D b, GeoVec2D c) {
        // NB temporary variables *crucial*: a and c can be the same variable
        //double x1=a.x,y1=a.y,x2=b.x,y2=b.y;
        //  do multiply
        //c.x = (x1 * x2 - y1 * y2);
        //c.y = (y2 * x1 + x2 * y1);

        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 */
    final public static void complexPower(GeoVec2D a, NumberValue b, GeoVec2D c) {
        Complex out = new Complex(a.x, a.y);
        out = out.pow(new Complex(b.getDouble(), 0));
        c.x = out.getReal();
        c.y = out.getImaginary();
        c.setMode(Kernel.COORD_COMPLEX);
    }

    /** c = sqrt(a) Michael Borcherds 2010-02-07 */
    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 = sqrt(a) Michael Borcherds 2010-02-07 */
    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 = sqrt(a) Michael Borcherds 2010-02-07 */
    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 = sqrt(a) Michael Borcherds 2010-02-07 */
    final public static double arg(GeoVec2D a) {
        return Math.atan2(a.y, a.x);
    }

    /** c = a ^ b Michael Borcherds 2009-03-10 */
    final public static void complexPower(NumberValue a, GeoVec2D b, GeoVec2D c) {
        Complex 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 */
    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 */
    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 */
    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 */
    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 */
    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()
     */
    final public static void vectorProduct(GeoVec2D a, GeoVec2D b, MyDouble c) {
        c.set(a.x * b.y - a.y * b.x);
    }

    final public static void inner(GeoVec2D a, GeoVec2D b, MyDouble c) {
        c.set(a.x * b.x + a.y * b.y);
    }

    /** c = a / b */
    final public static void div(GeoVec2D a, double b, GeoVec2D c) {
        c.x = a.x / b;
        c.y = a.y / b;
    }

    final public boolean isDefined() {
        return !(Double.isNaN(x) || Double.isNaN(y));
    }

    final public String toString() {
        if (isImaginaryUnit()) {
            switch (kernel.getCASPrintForm()) {
            case ExpressionNode.STRING_TYPE_MPREDUCE:
                return "i";

            default:
                //             case ExpressionNode.STRING_TYPE_GEOGEBRA:
                //             case ExpressionNode.STRING_TYPE_GEOGEBRA_XML:
                //             case ExpressionNode.STRING_TYPE_LATEX:
                return Unicode.IMAGINARY;
            }
        }

        sbToString.setLength(0);
        sbToString.append('(');
        sbToString.append(kernel.format(x));
        sbToString.append(", ");
        sbToString.append(kernel.format(y));
        sbToString.append(')');
        return sbToString.toString();
    }

    private StringBuilder sbToString = new StringBuilder(50);

    /**
     * interface VectorValue implementation
     */
    final public GeoVec2D getVector() {
        if (this.isImaginaryUnit())
            return new GeoVec2D(this);
        else
            return this;
    }

    final public boolean isConstant() {
        return true;
    }

    final public boolean isLeaf() {
        return true;
    }

    final public int getMode() {
        return mode;
    }

    final public ExpressionValue evaluate() {
        return getVector();
    }

    final public HashSet<GeoElement> getVariables() {
        return null;
    }

    final public void setMode(int mode) {
        this.mode = mode;
    }

    final public String toValueString() {
        return toString();
    }

    public String toLaTeXString(boolean symbolic) {
        return toString();
    }

    // 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;
    }
     */

    final public boolean isNumberValue() {
        return false;
    }

    final public boolean isVectorValue() {
        return true;
    }

    final public boolean isBooleanValue() {
        return false;
    }

    final public boolean isPolynomialInstance() {
        return false;
    }

    final public boolean isTextValue() {
        return false;
    }

    final public boolean isExpressionNode() {
        return false;
    }

    public boolean isListValue() {
        return false;
    }

    final public boolean contains(ExpressionValue ev) {
        return ev == this;
    }

    /** multiplies 2D vector by a 2x2 matrix
     * 
     * @param 2x2 matrix
     */
    public void multiplyMatrix(MyList list) {
        if (list.getMatrixCols() != 2 || list.getMatrixRows() != 2)
            return;

        double a, b, c, d, x1, y1;

        a = ((NumberValue) (MyList.getCell(list, 0, 0).evaluate())).getDouble();
        b = ((NumberValue) (MyList.getCell(list, 1, 0).evaluate())).getDouble();
        c = ((NumberValue) (MyList.getCell(list, 0, 1).evaluate())).getDouble();
        d = ((NumberValue) (MyList.getCell(list, 1, 1).evaluate())).getDouble();

        matrixTransform(a, b, c, d);
    }

    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;
    }

    public boolean isMatrixTransformable() {
        return true;
    }

    public GeoElement toGeoElement() {
        return null;
    }

    /** multiplies 2D vector by a 3x3 affine matrix
     *  a b c
     *  d e f
     *  g h i
     * @param 3x3 matrix
     * @param 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, x1, y1, z1, xx = x, yy = y, zz = 1;

        // use homogeneous coordinates if available
        if ((rt instanceof GeoPoint) || (rt instanceof GeoLine)) {
            GeoVec3D p = (GeoVec3D) rt;
            xx = p.x;
            yy = p.y;
            zz = p.z;
        }

        a = ((NumberValue) (MyList.getCell(list, 0, 0).evaluate())).getDouble();
        b = ((NumberValue) (MyList.getCell(list, 1, 0).evaluate())).getDouble();
        c = ((NumberValue) (MyList.getCell(list, 2, 0).evaluate())).getDouble();
        d = ((NumberValue) (MyList.getCell(list, 0, 1).evaluate())).getDouble();
        e = ((NumberValue) (MyList.getCell(list, 1, 1).evaluate())).getDouble();
        f = ((NumberValue) (MyList.getCell(list, 2, 1).evaluate())).getDouble();
        g = ((NumberValue) (MyList.getCell(list, 0, 2).evaluate())).getDouble();
        h = ((NumberValue) (MyList.getCell(list, 1, 2).evaluate())).getDouble();
        i = ((NumberValue) (MyList.getCell(list, 2, 2).evaluate())).getDouble();

        x1 = a * xx + b * yy + c * zz;
        y1 = d * xx + e * yy + f * zz;
        z1 = g * xx + h * yy + i * zz;

        x = x1 / z1;
        y = y1 / z1;
        return;
    }

    public void setZero() {
        x = 0;
        y = 0;
    }

    public boolean isVector3DValue() {
        // TODO Auto-generated method stub
        return false;
    }

    public String toOutputValueString() {
        return toValueString();
    }

    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;

    }

    public Kernel getKernel() {
        return kernel;
    }

}