package ProGAL.geom3d; import static java.lang.Math.cos; import static java.lang.Math.sin; import java.awt.Color; import ProGAL.geom3d.Point; //import ProGAL.geom3d.viewer.J3DScene; import ProGAL.geom3d.volumes.LSS; import ProGAL.math.Constants; import ProGAL.math.Matrix; /** * A vector in (x,y,z)-space represented with double precision. * @todo cache the length so several calls to getLengthSquared and getLength * takes less time. This can be relevant e.g. in Line3d for multiple * projections, but can also cause serious problems if anyone chooses to extend the * Vector3d object. */ public class Vector extends ProGAL.geomNd.Vector{ /** Construct a vector with the specified coordinates. */ public Vector(double x, double y, double z) { super(new double[]{x,y,z}); } /** Construct a vector pointing from origo to p. */ public Vector(Point p) { super(p); } /** Constructs a vector between two points p1 and p2 - added by pawel 12-11-2011 */ public Vector(Point p1, Point p2) { super(p1, p2); } /** Construct a vector that is a clone of v. */ public Vector(Vector v) { this(v.get(0), v.get(1), v.get(2)); } /** Construct a vector that is a clone of v. */ public Vector(ProGAL.geomNd.Vector v) { this(v.get(0), v.get(1), v.get(2)); } /** Construct a vector using the double-array as coordinates. Note: The array is not cloned */ public Vector(double[] coords) { super(coords); } /** Get the first coordinate. */ public double x() { return coords[0]; } /** Get the second coordinate. */ public double y() { return coords[1]; } /** Get the third coordinate. */ public double z() { return coords[2]; } /** Set the first coordinate */ public void setX(double x) { this.coords[0] = x; } /** Set the second coordinate */ public void setY(double y) { this.coords[1] = y; } /** Set the third coordinate */ public void setZ(double z) { this.coords[2] = z; } /** Set all coordinates of this vector equal to those of v */ public void set(Vector v) { this.coords[0] = v.coords[0]; this.coords[1] = v.coords[1]; this.coords[2] = v.coords[2]; } /** Get the dot-product of this vector and v. */ public double dot(Vector v){ return coords[0]*v.coords[0]+coords[1]*v.coords[1]+coords[2]*v.coords[2]; } /** Get the dot-product of this vector and position vector to point p. */ public double dot(Point p){ return coords[0]*p.x()+coords[1]*p.y()+coords[2]*p.z(); } /** Get the angle between this vector and v. */ public double angle(Vector v) { return Math.acos(dot(v)/(length()*v.length())); // return Math.acos(Math.min( 1, this.dot(v)/Math.sqrt(this.getLengthSquared()*v.getLengthSquared()) )); } /** Add v to this vector and return the result (without changing this object). */ public Vector add(Vector v){ return new Vector(coords[0]+v.coords[0],coords[1]+v.coords[1],coords[2]+v.coords[2]); } /** Add v to this vector and return the result (changing this object). */ public Vector addThis(Vector v){ coords[0]+=v.coords[0]; coords[1]+=v.coords[1]; coords[2]+=v.coords[2]; return this; } /** Add p to this vector and return the result (without changing this object). */ public Vector add(Point p){ return new Vector(coords[0]+p.x(),coords[1]+p.y(),coords[2]+p.z()); } /** Add p to this vector and return the result (changing this object). */ public Vector addThis(Point p){ coords[0]+=p.x(); coords[1]+=p.y(); coords[2]+=p.z(); return this; } /** Subtract v from this vector and return the result (without changing this object). */ public Vector subtract(Vector v){ return new Vector(coords[0]-v.coords[0],coords[1]-v.coords[1],coords[2]-v.coords[2]); } /** Subract v from this vector and return the result (changing this object). */ public Vector subtractThis(Vector v){ coords[0]-=v.coords[0]; coords[1]-=v.coords[1]; coords[2]-=v.coords[2]; return this; } /** Multiply this vector by s and return the result (without changing this object). */ public Vector multiply(double s){ return new Vector(coords[0]*s, coords[1]*s, coords[2]*s); } /** Multiply this vector by s and return the result (changing this object). */ public Vector multiplyThis(double s){ coords[0]*=s;coords[1]*=s;coords[2]*=s;return this; } //Daisy public Vector multiply(Vector v){ double[] ret = new double[dim]; for(int d=0;d>1); x = Double.longBitsToDouble(i); x = x*(1.5d - xhalf*x*x); return x; } /** Scale this vector to a certain length (returns new object and does not change this object). */ public Vector scaleToLength(double length) { return multiply(length/length()); } /** Scale this vector to a certain length (changes this object). */ public Vector scaleToLengthThis(double length) { return multiplyThis(length/length()); } /** Get the cross-product of this vector and v (without changing this object). */ public Vector cross(Vector v){ return new Vector(coords[1]*v.coords[2] - coords[2]*v.coords[1], coords[2]*v.coords[0] - coords[0]*v.coords[2], coords[0]*v.coords[1] - coords[1]*v.coords[0]); } /** Get the cross-product of this vector and v and store the result in this vector (changes this object). */ public Vector crossThis(Vector v){ double newX = coords[1]*v.coords[2] - coords[2]*v.coords[1], newY = coords[2]*v.coords[0] - coords[0]*v.coords[2], newZ = coords[0]*v.coords[1] - coords[1]*v.coords[0]; this.coords[0] = newX;this.coords[1] = newY;this.coords[2] = newZ; return this; } /** rotates (clockwise) the vector around the line through the origo with the direction unit vector v. * For counterclockwise rotation change signs within parentheses in non-diagonal terms. */ public void rotation(Vector v, double alpha) { double c = Math.cos(alpha); double d = 1.0-c; double s = Math.sin(alpha); double vxyd = v.x()*v.y()*d, vxzd = v.x()*v.z()*d, vyzd = v.y()*v.z()*d; double vxs = v.x()*s, vys = v.y()*s, vzs = v.z()*s; double xNew = (v.x()*v.x()*d+c)*x() + (vxyd-vzs)*y() + (vxzd+vys)*z(); double yNew = (vxyd+vzs)*x() + (v.y()*v.y()*d+c)*y() + (vyzd-vxs)*z(); setZ((vxzd-vys)*x() + (vyzd+vxs)*y() + (v.z()*v.z()*d+c)*z()); setX(xNew); setY(yNew); } /** * Perform a right-handed rotation of v around this vector. * TODO: Test */ public Vector rotateIn(Vector v, double angle) { double l = length(); if(l==0) throw new Error("Trying to rotate around 0-vector"); double ux = coords[0]/l; double uy = coords[1]/l; double uz = coords[2]/l; double sin = sin(angle); double cos = cos(angle); double a00 = (ux*ux + cos*(1.0-ux*ux)); double a10 = (ux*uy*(1.0-cos)+uz*sin); double a20 = (uz*ux*(1-cos) - uy*sin); double a01 = (ux*uy*(1-cos) - uz*sin); double a11 = (uy*uy + cos*(1.0-uy*uy)); double a21 = (uy*uz*(1.0-cos) + ux*sin); double a02 = (uz*ux*(1.0-cos) + uy*sin); double a12 = (uy*uz*(1.0-cos) - ux*sin); double a22 = (uz*uz + cos*(1.0 - uz*uz)); double newX = a00*v.coords[0]+a01*v.coords[1]+a02*v.coords[2]; double newY = a10*v.coords[0]+a11*v.coords[1]+a12*v.coords[2]; double newZ = a20*v.coords[0]+a21*v.coords[1]+a22*v.coords[2]; v.setX(newX); v.setY(newY); v.setZ(newZ); return v; } /** Convert this vector to a point. */ public Point toPoint() { return new Point(coords[0], coords[1], coords[2]); } /** Returns a string-representation of this vector formatted with two decimals precision. */ public String toString() { return toString(2); } /** Returns a string-representation of this vector formatted with dec decimals precision. */ public String toString(int dec) { return String.format("Vector3d[%."+dec+"f,%."+dec+"f,%."+dec+"f]",coords[0],coords[1],coords[2]); } /** Writes this vector to System.out. */ public void toConsole() { toConsole(2); } /** Writes this vector to System.out with dec decimals precision. */ public void toConsole(int dec) { System.out.println(toString(dec)); } // /** Draws a vector starting at the origin */ // public LSS toScene(J3DScene scene, Color clr, double width) { // LSS lss = new LSS(new Point(0,0,0), toPoint(), width); // scene.addShape(lss, clr, 3); // return lss; // } // /** Draws a vector starting at point p */ // public LSS toScene(J3DScene scene, Point p, Color clr, double width) { // LSS lss = new LSS(p, p.add(this), width); // scene.addShape(lss, clr, 3); // return lss; // } /** Create a clone of this vector. */ public Vector clone(){ return new Vector(coords[0], coords[1], coords[2]); } ///////// Static methods and fields //////// /** Get the angle between vector u and v. */ public static double getAngle(Vector u, Vector v) { return u.angle(v); } /** Get the dihedral angle between 3 non-colinear vectors b1, b2, b3. */ public static double getDihedralAngle(Vector b1, Vector b2, Vector b3) { Vector b2xb3 = b2.cross(b3); double y = b1.multiply(b2.length()).dot(b2xb3); double x = b1.cross(b2).dot(b2xb3); return Math.atan2(y,x); } /** Get a vector (one of many possible) orthonormal to vector v (v is assumed to be unit a vector). */ public Vector getOrthonormal() { if (Math.abs(z()) > Constants.EPSILON) { double ratio = x()/z(); double length = Math.sqrt(1 + ratio*ratio); return new Vector(1/length, 0, -ratio/length); } else return new Vector(0, 0, 1); } /** Get a vector (one of many possible) orthogonal (i.e., having arbitrary length) to vector v. */ public Vector getOrthogonal() { if (Math.abs(z()) > Constants.EPSILON) return new Vector(1, 0, -x()/z()); else return new Vector(0, 0, 1); } /* * added by pawel 12-11-2011 */ public boolean isParallel(Vector v) { return cross(v).isZeroVector(); } public boolean isSteinerAngle(Vector v) { return dot(v)/(this.length()*v.length()) > -0.5; } /* * returns cosinus of the dihedral angle between three vectors * added by pawel on 12-11-2011 */ public static double getCosDihedralAngle(Vector u, Vector v, Vector w) { if (u.isParallel(v)) throw new Error("Vectors u and v are colinear"); if (v.isParallel(w)) throw new Error("Vectors v and w are colinear"); Vector uv = u.cross(u.add(v)); Vector vw = v.cross(v.add(w)); return (uv.dot(vw)/(uv.length()*vw.length())); } /** An immutable vector pointing in the (1,0,0)-direction. */ public static Vector X = new ImmutableVector3d(1,0,0); /** An immutable vector pointing in the (0,1,0)-direction. */ public static Vector Y = new ImmutableVector3d(0,1,0); /** An immutable vector pointing in the (0,0,1)-direction. */ public static Vector Z = new ImmutableVector3d(0,0,1); ///////// Inner classes //////// /** * A wrapper class for Vector3d which makes the vector immutable. * All methods that can change the x, y and z-coordinates are overwritten. If * e.g. setX(0.1) is called a RuntimeException is thrown. If any of the * arithmetic methods such as multiplyThis are called, the result of * their corresponding non-mutating variant (multiply) is returned instead. */ public static class ImmutableVector3d extends Vector{ public ImmutableVector3d(double x, double y, double z) { super(x,y,z); } public void setCoord(int i, double v){throw new RuntimeException("This vector is immutable");} public void set(int i, double v){throw new RuntimeException("This vector is immutable");} public void setX(double x) { throw new RuntimeException("This vector is immutable"); } public void setY(double y) { throw new RuntimeException("This vector is immutable"); } public void setZ(double z) { throw new RuntimeException("This vector is immutable"); } public Vector addThis(Vector v){ return add(v); } public Vector multiplyThis(double s){ return multiply(s); } public Vector normalizeThis(){ return multiply(1/length()); } public Vector scaleToLengthThis(double length) { return multiply(length/length()); } public Vector crossThis(Vector v){ return cross(v); } } public static void main(String[] args) { Vector a = new Vector(0,0,1); Vector b = new Vector(1,0,1); Point center = new Point(1,1,0); Circle C = new Circle(center, 0.2, b); Vector rotAxis = b.cross(a); double angle = b.angle(a); // double angle = b.dot(a.toPoint()); Matrix rotMatrix = Matrix.createRotationMatrix(angle, rotAxis); Vector translate = new Vector(-center.x(),-center.y(),-center.z()); Vector newVector = rotMatrix.multiply(b); Circle newC = new Circle(center.add(translate), 0.2, newVector.normalize()); System.out.println("newC = "+newC.toString()); Vector n = new Vector(0, 0, 1); Vector u = new Vector(0.3, 0.4, 0.0).normalize(); Vector v = n.cross(u); System.out.println(u.x() + " " + v.y()); System.out.println("cross = "+n.cross(n)); } }