/* 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. */ /* * GeoVec3D.java * * Created on 31. August 2001, 11:22 */ package org.geogebra.common.kernel.geos; import org.geogebra.common.kernel.Construction; 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.NumberValue; import org.geogebra.common.kernel.kernelND.CoordStyle; import org.geogebra.common.kernel.kernelND.GeoElementND; /** * * @author Markus */ public abstract class GeoVec3D extends GeoElement implements Traceable, CoordStyle { /** x coordinate */ public double x = Double.NaN; /** y coordinate */ public double y = Double.NaN; /** z coordinate */ public double z = Double.NaN; private boolean trace; /** * For backward compatibility */ public boolean hasUpdatePrevilege = false; /** * @param c * construction */ public GeoVec3D(Construction c) { super(c); } /** * Creates new GeoVec3D with coordinates (x,y,z) and label * * @param c * construction * @param x * x-coord * @param y * y-coord * @param z * z-coord */ public GeoVec3D(Construction c, double x, double y, double z) { this(c); setCoords(x, y, z); } @Override public boolean isDefined() { return (!(Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z))); } @Override public void setUndefined() { setUndefinedCoords(); update(); // TODO hide undefined elements in algebraView } /** * set undefined coords */ protected void setUndefinedCoords() { setCoords(Double.NaN, Double.NaN, Double.NaN); } @Override protected boolean showInEuclidianView() { return isDefined(); } @Override public boolean showInAlgebraView() { // return true; // return isDefined(); return true; } @Override public void set(GeoElementND geo) { if (geo instanceof GeoVec3D) { GeoVec3D v = (GeoVec3D) geo; setCoords(v.x, v.y, v.z); } else { setUndefined(); } } /** * @param x * x-coord * @param y * y-coord * @param z * z-coord */ public abstract void setCoords(double x, double y, double z); /** * Set coords from source vector * * @param v * source vector */ public abstract void setCoords(GeoVec3D v); /** * @return x-coord */ final public double getX() { return x; } /** * @return y-coord */ final public double getY() { return y; } /** * @return z-coord */ final public double getZ() { return z; } /** * @param ret * array to store coords */ final public void getCoords(double[] ret) { ret[0] = x; ret[1] = y; ret[2] = z; } /** * @return this vector as coords */ final public Coords getCoords() { Coords coords = new Coords(x, y, z); return coords; } /** * Writes x and y to the array res. * * @param res * array to store x and y */ public void getInhomCoords(double[] res) { res[0] = x; res[1] = y; } // POLAR or CARTESIAN mode /** * @return true if using POLAR style */ final public boolean isPolar() { return toStringMode == Kernel.COORD_POLAR; } /** * @return currently used coordstyle */ @Override public int getMode() { return toStringMode; } /** * Sets the coord style * * @param mode * new coord style */ @Override public void setMode(int mode) { toStringMode = mode; } /** * Changes coord style to POLAR */ @Override public void setPolar() { toStringMode = Kernel.COORD_POLAR; } /** * Changes coord style to CARTESIAN */ @Override public void setCartesian() { toStringMode = Kernel.COORD_CARTESIAN; } /** * Changes coord style to COMPLEX */ @Override public void setComplex() { toStringMode = Kernel.COORD_COMPLEX; } /** * Changes coord style to CARTESIAN 3D */ @Override public void setCartesian3D() { toStringMode = Kernel.COORD_CARTESIAN_3D; } @Override public void setSpherical() { setMode(Kernel.COORD_SPHERICAL); } @Override public boolean isTraceable() { return true; } @Override public void setTrace(boolean trace) { this.trace = trace; } @Override public boolean getTrace() { return trace; } // G.Sturr 2010-5-14: no longer needed /* * public void setSpreadsheetTrace(boolean spreadsheetTrace) { * this.spreadsheetTrace = spreadsheetTrace; * * if (spreadsheetTrace) resetTraceColumns(); } * * * * public boolean getSpreadsheetTrace() { return spreadsheetTrace; } * */ // END G.Sturr /** * Yields true if this vector and v are linear dependent This is done by * calculating the cross product of this vector an v: this lin.dep. v * <=> this.cross(v) = nullvector. * * @param v * other vector * @return true if this and other vector are linear dependent */ final public boolean linDep(GeoVec3D v) { // v lin.dep this <=> angle(v,w) ~ 0 <=> cross(v,w) << |this|*|v| // use Math.max(abs coords) as norm to avoid overflow / underflow double n1 = Math.max(Math.abs(x), Math.max(Math.abs(y), Math.abs(z))); double n2 = Math.max(Math.abs(v.x), Math.max(Math.abs(v.y), Math.abs(v.z))); double x1 = x / n1; double y1 = y / n1; double z1 = z / n1; double x2 = v.x / n2; double y2 = v.y / n2; double z2 = v.z / n2; return Kernel.isEqual(x1 * y2, x2 * y1) && Kernel.isEqual(z1 * y2, z2 * y1) && Kernel.isEqual(x1 * z2, x2 * z1); } /** * @return tue if all coords are zero */ final public boolean isZero() { return Kernel.isZero(x) && Kernel.isZero(y) && Kernel.isZero(z); } /** * Calculates the cross product of this vector and vector v. The result ist * returned as a new GeoVec3D. */ // final public GeoVec3D cross(GeoVec3D v) { // GeoVec3D res = new GeoVec3D(v.cons); // cross(this, v, res); // return res; // } /** * Calculates the cross product of vectors u and v. The result is stored in * w. * * @param u * vector u * @param v * vector v * @param w * vector to store u x v */ final public static void cross(GeoVec3D u, GeoVec3D v, GeoVec3D w) { w.setCoords(u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x); } /** * Calculates the cross product of vectors u and v. * * @param u * vector u * @param v * vector v * @return the cross product of vectors u and v. */ final public static Coords cross(GeoVec3D u, GeoVec3D v) { Coords ret = new Coords(3); ret.setX(u.y * v.z - u.z * v.y); ret.setY(u.z * v.x - u.x * v.z); ret.setZ(u.x * v.y - u.y * v.x); return ret; } /** * Calculates the line through the points A and B. The result is stored in * g. * * @param A * first point * @param B * second point * @param g * line to store the result */ final public static void lineThroughPoints(GeoPoint A, GeoPoint B, GeoLine g) { // note: this could be done simply using cross(A, B, g) // but we want to avoid large coefficients in the line // and we want AB to be the direction vector of the line if (!(A.isDefined() && B.isDefined())) { g.setUndefined(); return; } if (A.isInfinite()) {// A is direction if (B.isInfinite()) { // g is undefined g.setUndefined(); } else { // through point B g.setCoords(A.getY(), -A.getX(), A.getX() * B.getInhomY() - A.getY() * B.getInhomX()); } } else { // through point A if (B.isInfinite()) { // B is direction g.setCoords(-B.getY(), B.getX(), A.getInhomX() * B.getY() - A.getInhomY() * B.getX()); } else { // through point B g.setCoords(A.getInhomY() - B.getInhomY(), B.getInhomX() - A.getInhomX(), A.getInhomX() * B.getInhomY() - A.getInhomY() * B.getInhomX()); } } } /** * Calculates the line through the points A and B. The result is stored in * g. * * @param A * first point * @param B * second point * @param g * line to store result */ final public static void lineThroughPointsCoords(Coords A, Coords B, GeoLine g) { // note: this could be done simply using cross(A, B, g) // but we want to avoid large coefficients in the line // and we want AB to be the direction vector of the line if (!(A.getLength() == 3 && B.getLength() == 3)) { g.setUndefined(); return; } if (!(A.isFinite() && B.isFinite())) { g.setUndefined(); return; } if (Kernel.isZero(A.getZ())) {// A is direction if (Kernel.isZero(B.getZ())) { // g is undefined g.setUndefined(); } else { // through point B g.setCoords(A.getY(), -A.getX(), A.getX() * B.getInhom(1) - A.getY() * B.getInhom(0)); } } else { // through point A if (Kernel.isZero(B.getZ())) { // B is direction g.setCoords(B.getY(), -B.getX(), B.getX() * A.getInhom(1) - B.getY() * A.getInhom(0)); } else { // through point B double aInhomX = A.getInhom(0); double aInhomY = A.getInhom(1); double bInhomX = B.getInhom(0); double bInhomY = B.getInhom(1); g.setCoords(aInhomY - bInhomY, bInhomX - aInhomX, aInhomX * bInhomY - aInhomY * bInhomX); } } } /** * Calculates the line through the point A with direction v. The result is * stored in g. * * @param A * start point * @param v * direction vector * @param g * line to store result */ final public static void lineThroughPointVector(GeoPoint A, GeoVec3D v, GeoLine g) { // note: this could be done simply using cross(A, v, g) // but we want to avoid large coefficients in the line // and we want v to be the direction vector of the line if (A.isInfinite()) {// A is direction g.setUndefined(); } else { // through point A // v is direction g.setCoords(-v.y, v.x, A.getInhomX() * v.y - A.getInhomY() * v.x); } } /** * Calculates the cross product of vectors u and v. The result is stored in * w. * * @param u * vector u * @param vx * x(v) * @param vy * y(v) * @param vz * z(v) * @param w * vector to store u * v */ final public static void cross(GeoVec3D u, double vx, double vy, double vz, GeoVec3D w) { w.setCoords(u.y * vz - u.z * vy, u.z * vx - u.x * vz, u.x * vy - u.y * vx); } /** * Calculates the cross product of vectors u and v. The result is stored in * w. * * @param ux * x(u) * @param uy * y(u) * @param uz * z(u) * @param vx * x(v) * @param vy * y(v) * @param vz * z(v) * @param w * vector to store u*v */ final public static void cross(double ux, double uy, double uz, double vx, double vy, double vz, GeoVec3D w) { w.setCoords(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx); } /** * Calculates the cross product of vectors u and v. The result is stored in * w. * * @param u * first input * @param v * second input * @param w * vector to store u*v */ final public static void cross(double[] u, double[] v, double[] w) { w[0] = u[1] * v[2] - u[2] * v[1]; w[1] = u[2] * v[0] - u[0] * v[2]; w[2] = u[0] * v[1] - u[1] * v[0]; } /** * Calculates the inner product of this vector and vector v. * * @param v * other vector * @return inner product */ final public double inner(GeoVec3D v) { return x * v.x + y * v.y + z * v.z; } /** * Changes orientation of this vector. v is changed to -v. */ final public void changeSign() { setCoords(-x, -y, -z); } /** * returns -v */ // final public GeoVec3D getNegVec() { // return new GeoVec3D(cons, -x, -y, -z); // } /** returns this + a */ // final public GeoVec3D add(GeoVec3D a) { // GeoVec3D res = new GeoVec3D(cons); // add(this, a, res); // return res; // } /** * c = a + b * * @param a * vector a * @param b * vector b * @param c * vector to store a+b **/ final public static void add(GeoVec3D a, GeoVec3D b, GeoVec3D c) { c.setCoords(a.x + b.x, a.y + b.y, a.z + b.z); } /** returns this - a */ // final public GeoVec3D sub(GeoVec3D a) { // GeoVec3D res = new GeoVec3D(cons); // sub(this, a, res); // return res; // } /** * c = a - b * * @param a * vector a * @param b * vector b * @param c * vector to store a-b */ final public static void sub(GeoVec3D a, GeoVec3D b, GeoVec3D c) { c.setCoords(a.x - b.x, a.y - b.y, a.z - b.z); } @Override public String toString(StringTemplate tpl) { sbToString.setLength(0); sbToString.append('('); sbToString.append(x); sbToString.append(", "); sbToString.append(y); sbToString.append(", "); sbToString.append(z); sbToString.append(')'); return sbToString.toString(); } private StringBuilder sbToString = new StringBuilder(50); /** * returns all class-specific xml tags for saveXML Geogebra File Format */ @Override protected void getXMLtags(StringBuilder sb) { super.getXMLtags(sb); sb.append("\t<coords"); sb.append(" x=\""); sb.append(x); sb.append("\""); sb.append(" y=\""); sb.append(y); sb.append("\""); sb.append(" z=\""); sb.append(z); sb.append("\""); sb.append("/>\n"); } @Override public void getXMLtagsMinimal(StringBuilder sb, StringTemplate tpl) { sb.append(regrFormat(x) + " " + regrFormat(y) + " " + regrFormat(z)); } @Override public boolean isNumberValue() { return false; } @Override public void setZero() { x = 0; y = 0; z = 0; } /** * @param phi * angle of rotation */ protected void rotateXY(NumberValue phi) { double ph = phi.getDouble(); double cos = Math.cos(ph); double sin = Math.sin(ph); double x0 = x * cos - y * sin; y = x * sin + y * cos; x = x0; } /** * mirror transform with angle phi [ cos(phi) sin(phi) ] [ sin(phi) * -cos(phi) ] * * @param phi * parameter of mirror transform */ protected final void mirrorXY(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; } @Override public boolean hasCoords() { return true; } }