/*
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.geogebra3D.kernel3D.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.MyDouble;
import org.geogebra.common.kernel.arithmetic.MyList;
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.arithmetic3D.Vector3DValue;
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.kernel.geos.GeoVec3D;
import org.geogebra.common.kernel.kernelND.Geo3DVecInterface;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoVecInterface;
/**
*
* @author Michael adapted from GeoVec2D
*/
final public class Geo3DVec extends ValidExpression
implements Vector3DValue, Geo3DVecInterface {
private double x = Double.NaN;
private double y = Double.NaN;
private double z = Double.NaN;
private int mode = Kernel.COORD_CARTESIAN_3D;
private Kernel kernel;
/**
* Creates new GeoVec2D
*
* @param kernel
* kernel
*/
public Geo3DVec(Kernel kernel) {
this.kernel = kernel;
}
/**
* Creates new GeoVec3D with coordinates (x,y)
*
* @param kernel
* kernel
* @param x
* x-coord
* @param y
* y-coord
* @param z
* z-coord
*/
public Geo3DVec(Kernel kernel, double x, double y, double z) {
this(kernel);
this.x = x;
this.y = y;
this.z = z;
}
/**
* Copy constructor
*
* @param v
* original
*/
public Geo3DVec(Geo3DVec v) {
this(v.kernel);
x = v.x;
y = v.y;
z = v.z;
mode = v.mode;
}
@Override
public Geo3DVec deepCopy(Kernel kernel1) {
return new Geo3DVec(this);
}
@Override
public void resolveVariables(EvalInfo info) {
// no variables ?
}
/**
* Creates new GeoVec3D as vector between Points P and Q
*
* @param kernel
* kernel
* @param p
* start point
* @param q
* end point
*/
public Geo3DVec(Kernel kernel, GeoPoint3D p, GeoPoint3D q) {
this(kernel);
x = q.getX() - p.getX();
y = q.getY() - p.getY();
z = q.getZ() - p.getZ();
}
/**
* @param x
* x-coord
*/
public void setX(double x) {
this.x = x;
}
/**
* @param y
* y-coord
*/
public void setY(double y) {
this.y = y;
}
/**
* @param z
* z-coord
*/
public void setZ(double z) {
this.z = z;
}
/**
* @param x
* x-coord
* @param y
* y-coord
* @param z
* z-coord
*/
public void setCoords(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* @param a
* [x,y,z], further elements ignored
*/
public void setCoords(double[] a) {
x = a[0];
y = a[1];
z = a[2];
}
/**
* Copy coords from source to this
*
* @param v
* source vector
*/
public void setCoords(GeoVec3D v) {
x = v.x;
y = v.y;
z = v.z;
}
@Override
final public double getX() {
return x;
}
@Override
final public double getY() {
return y;
}
@Override
final public double getZ() {
return z;
}
// final public double getR() { return length(x, y); }
// final public double getPhi() { return Math.atan2(y, x); }
/**
* Calculates the eucilidian length of this 2D vector. The result is
* sqrt(x^2 + y^2).
*/
@Override
final public double length() {
return length(x, y, z);
}
/**
* Calculates the euclidian length sqrt(a^2 + b^2).
*/
private static double length(double a, double b, double c) {
return Math.sqrt(a * a + b * b + c * c);
}
/**
* 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;
}
/**
* Yields true if the coordinates of this vector are equal to those of
* vector v.
*
* @param v
* other vector
* @return whether they are equal, ignoring z
*/
final public boolean equals(GeoVec2D v) {
return Kernel.isEqual(x, v.getX()) && Kernel.isEqual(y, v.getY());
}
/**
* 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
*/
/** 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
*
* @param a
* addend
* @param b
* addend
* @param c
* sum
*/
final public static void add(Geo3DVec a, Geo3DVec b, Geo3DVec c) {
c.x = a.x + b.x;
c.y = a.y + b.y;
c.z = a.z + b.z;
}
/**
* c = a + b
*
* @param a
* addend
* @param b
* addend
* @param c
* sum
*/
final public static void add(Geo3DVec a, GeoVec2D b, Geo3DVec c) {
c.x = a.x + b.getX();
c.y = a.y + b.getY();
c.z = a.z;
}
/**
* c = Vector (Cross) Product of a and b
*
* @param a
* vector
* @param b
* vector
* @param c
* product
*/
final public static void vectorProduct(GeoVecInterface a, GeoVecInterface b,
Geo3DVec c) {
// tempX/Y needed because a and c can be the same variable
double tempX = a.getY() * b.getZ() - a.getZ() * b.getY();
double tempY = -a.getX() * b.getZ() + a.getZ() * b.getX();
c.z = a.getX() * b.getY() - a.getY() * b.getX();
c.x = tempX;
c.y = tempY;
}
/** 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
*
* @param a
* vector
* @param b
* vector
* @param c
* result
*/
final public static void sub(Geo3DVec a, Geo3DVec b, Geo3DVec c) {
c.x = a.x - b.x;
c.y = a.y - b.y;
c.z = a.z - b.z;
}
/**
* c = a - b
*
* @param a
* vector
*
* @param b
* vector
* @param c
* result
* */
final public static void sub(Geo3DVec a, GeoVec2D b, Geo3DVec c) {
c.x = a.x - b.getX();
c.y = a.y - b.getY();
c.z = a.z;
}
/**
* c = a - b
*
* @param a
* vector
* @param b
* vector
* @param c
* result
*/
final public static void sub(GeoVec2D a, Geo3DVec b, Geo3DVec c) {
c.x = a.getX() - b.x;
c.y = a.getY() - b.y;
c.z = -b.z;
}
/**
* c = a * b
*
* @param a
* factor
* @param b
* factor
* @param c
* product
*/
final public static void mult(Geo3DVec a, double b, Geo3DVec c) {
c.x = a.x * b;
c.y = a.y * b;
c.z = a.z * b;
}
/**
* Store inner product of two vectors in a number
*
* @param a
* 1st vector
* @param b
* 2nd vector
* @param c
* output number
*/
final public static void inner(GeoVecInterface a, GeoVecInterface b,
MyDouble c) {
c.set(a.getX() * b.getX() + a.getY() * b.getY() + a.getZ() * b.getZ());
}
/**
* Multiplies two vectors as complex numbers. Returns undefined if they are
* not 2D (z=0)
*
* @param a
* factor
* @param b
* factor
* @param c
* product
*/
final public static void complexMultiply(GeoVecInterface a,
GeoVecInterface b, GeoVec2D c) {
if (!Kernel.isZero(a.getZ()) || !Kernel.isZero(b.getZ())) {
c.setX(Double.NaN);
c.setY(Double.NaN);
c.setMode(Kernel.COORD_COMPLEX);
return;
}
Complex out = new Complex(a.getX(), a.getY());
out = out.multiply(new Complex(b.getX(), b.getY()));
c.setX(out.getReal());
c.setY(out.getImaginary());
c.setMode(Kernel.COORD_COMPLEX);
}
/**
* c = a / b
*
* @param a
* dividend
* @param b
* divisor
* @param c
* ratio
*/
final public static void div(Geo3DVec a, double b, Geo3DVec c) {
c.x = a.x / b;
c.y = a.y / b;
c.z = a.z / b;
}
@Override
final public String toString(StringTemplate tpl) {
sbToString.setLength(0);
sbToString.append('(');
sbToString.append(kernel.format(x, tpl));
sbToString.append(", ");
sbToString.append(kernel.format(y, tpl));
sbToString.append(')');
return sbToString.toString();
}
private StringBuilder sbToString = new StringBuilder(50);
/**
* interface VectorValue implementation
*/
@Override
final public Geo3DVec getVector() {
return this;
}
@Override
final public boolean isConstant() {
return true;
}
@Override
final public boolean isLeaf() {
return true;
}
@Override
final public HashSet<GeoElement> getVariables() {
return null;
}
@Override
final public String toValueString(StringTemplate tpl) {
return toString(tpl);
}
@Override
public String toLaTeXString(boolean symbolic, StringTemplate tpl) {
return toString(tpl);
}
@Override
final public boolean isNumberValue() {
return false;
}
@Override
final public boolean contains(ExpressionValue ev) {
return ev == this;
}
@Override
public double[] getPointAsDouble() {
return new double[] { getX(), getY(), getZ() };
}
@Override
public String toOutputValueString(StringTemplate tpl) {
return toValueString(tpl);
}
@Override
public boolean isEqual(Geo3DVecInterface vec) {
Geo3DVec v = (Geo3DVec) vec;
return Kernel.isEqual(x, v.x) && Kernel.isEqual(y, v.y)
&& Kernel.isEqual(z, v.z);
}
/**
* multiplies 3D vector/point by a 3x3 matrix a b c d e f g h i
*
* @param list
* 3x3 matrix
* @param rt
* VectorNDValue (as ExpressionValue) to get coords from
*/
public void multiplyMatrix3x3(MyList list, VectorNDValue rt) {
double a, b, c, d, e, f, g, h, i, xx, yy, zz;
GeoVecInterface v = rt.getVector();
xx = v.getX();
yy = v.getY();
zz = v.getZ();
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;
z = g * xx + h * yy + i * zz;
}
/**
* multiplies 3D vector/point by a 4x4 matrix a b c d e f g h i
*
* @param list
* 4x4 matrix
* @param rt
* VectorNDValue (as ExpressionValue) to get coords from
*/
public void multiplyMatrix4x4(MyList list, VectorNDValue rt) {
double m, n, o, p, xx, yy, zz, ww;
boolean vector = false;
if (rt instanceof GeoPointND) { // 3D point
GeoPointND point = (GeoPointND) rt;
// use homogeneous coordinates
Coords coords = point.getCoordsInD3();
xx = coords.getX();
yy = coords.getY();
zz = coords.getZ();
ww = coords.getW();
} else {
GeoVecInterface v = rt.getVector();
xx = v.getX();
yy = v.getY();
zz = v.getZ();
ww = 0;
vector = true;
}
m = MyList.getCell(list, 0, 3).evaluateDouble();
n = MyList.getCell(list, 1, 3).evaluateDouble();
o = MyList.getCell(list, 2, 3).evaluateDouble();
p = MyList.getCell(list, 3, 3).evaluateDouble();
double w = m * xx + n * yy + o * zz + p * ww;
if (vector && !Kernel.isZero(w)) {
x = Double.NaN;
y = Double.NaN;
z = Double.NaN;
return;
}
double a, b, c, d, e, f, g, h, i, j, k, l;
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, 3, 0).evaluateDouble();
e = MyList.getCell(list, 0, 1).evaluateDouble();
f = MyList.getCell(list, 1, 1).evaluateDouble();
g = MyList.getCell(list, 2, 1).evaluateDouble();
h = MyList.getCell(list, 3, 1).evaluateDouble();
i = MyList.getCell(list, 0, 2).evaluateDouble();
j = MyList.getCell(list, 1, 2).evaluateDouble();
k = MyList.getCell(list, 2, 2).evaluateDouble();
l = MyList.getCell(list, 3, 2).evaluateDouble();
x = a * xx + b * yy + c * zz + d * ww;
y = e * xx + f * yy + g * zz + h * ww;
z = i * xx + j * yy + k * zz + l * ww;
if (!vector) {
x = x / w;
y = y / w;
z = z / w;
}
}
/**
* multiplies 3D vector/point by a 2x3 matrix a b d e g h
*
* @param list
* 2x3 matrix
* @param rt
* VectorNDValue (as ExpressionValue) to get coords from
*/
public void multiplyMatrix3x2(MyList list, VectorNDValue rt) {
double a, b, d, e, g, h, xx, yy;
GeoVecInterface v = rt.getVector();
xx = v.getX();
yy = v.getY();
a = MyList.getCell(list, 0, 0).evaluateDouble();
b = MyList.getCell(list, 1, 0).evaluateDouble();
d = MyList.getCell(list, 0, 1).evaluateDouble();
e = MyList.getCell(list, 1, 1).evaluateDouble();
g = MyList.getCell(list, 0, 2).evaluateDouble();
h = MyList.getCell(list, 1, 2).evaluateDouble();
x = a * xx + b * yy;
y = d * xx + e * yy;
z = g * xx + h * yy;
}
/**
* multiplies 3D vector/point by a 2x3 matrix a b c d e f
*
* @param list
* 2x3 matrix
* @param rt
* VectorNDValue (as ExpressionValue) to get coords from
* @param ret
* 2D vector / point with computed coords
*/
static public void multiplyMatrix(MyList list, VectorNDValue rt,
GeoVec2D ret) {
double a, b, c, d, e, f, xx, yy, zz;
GeoVecInterface v = rt.getVector();
xx = v.getX();
yy = v.getY();
zz = v.getZ();
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();
ret.setCoords(a * xx + b * yy + c * zz, d * xx + e * yy + f * zz);
}
@Override
public int getMode() {
return this.mode;
}
@Override
public Geo3DVec round() {
return new Geo3DVec(kernel, Math.round(x), Math.round(y),
Math.round(z));
}
@Override
public Geo3DVec floor() {
return new Geo3DVec(kernel, Math.floor(x), Math.floor(y),
Math.floor(z));
}
@Override
public Geo3DVec ceil() {
return new Geo3DVec(kernel, Math.ceil(x), Math.ceil(y), Math.ceil(z));
}
@Override
public ExpressionNode wrap() {
return new ExpressionNode(kernel, this);
}
@Override
public double arg() {
return Math.atan2(y, x);
}
@Override
public ValueType getValueType() {
return ValueType.VECTOR3D;
}
@Override
public int getDimension() {
return 3;
}
@Override
public void mult(double d) {
this.x *= d;
this.y *= d;
this.z *= d;
}
@Override
public void setMode(int mode) {
this.mode = mode;
}
@Override
public ExpressionValue getUndefinedCopy(Kernel kernel1) {
return kernel1.getManager3D().newGeo3DVec(Double.NaN, Double.NaN,
Double.NaN);
}
}