/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.util; /** MutableDouble2D is more or less the same class as java.awt.geom.Point2D.Double, except that it is hash-equivalent to Double2D. <p>Just as with MutableInt2D: you use MutableDouble2D as a STORED hash key at your peril: it has the same misfeature as java.awt.geom.Point2D.Double, and you should read the warning in Double2D. However, you can look up Double2D-keyed objects in a hash table by passing in a MutableDouble2D instead. */ public final class MutableDouble2D implements java.io.Serializable, Cloneable { private static final long serialVersionUID = 1; public double x; public double y; public MutableDouble2D() { x = 0.0; y = 0.0; } public MutableDouble2D(final Int2D p) { x = p.x; y = p.y; } public MutableDouble2D(final MutableInt2D p) { x = p.x; y = p.y; } public MutableDouble2D(final MutableDouble2D p) { x = p.x; y = p.y; } public MutableDouble2D(final Double2D p) { x = p.x; y = p.y; } public MutableDouble2D(final java.awt.Point p) { x = p.x; y = p.y; } public MutableDouble2D(final java.awt.geom.Point2D.Double p) { x = p.x; y = p.y; } public MutableDouble2D(final java.awt.geom.Point2D.Float p) { x = p.x; y = p.y; } /** Only included for completeness' sakes, in case a new Point2D subclass is created in the future. */ public MutableDouble2D(final java.awt.geom.Point2D p) { x = p.getX(); y = p.getY(); } public MutableDouble2D(final double x, final double y) { this.x = x; this.y = y; } public final double getX() { return x; } public final double getY() { return y; } public final void setX(double val) { x = val; } public final void setY(double val) { y = val; } public final void setTo(final double bx, final double by) { x = bx; y = by; } public final void setTo(final Int2D b) { x = b.x; y = b.y; } public final void setTo(final Double2D b) { x = b.x; y = b.y; } public final void setTo(final MutableInt2D b) { x = b.x; y = b.y; } public final void setTo(final MutableDouble2D b) { x = b.x; y = b.y; } public String toString() { return "MutableDouble2D["+x+","+y+"]"; } public String toCoordinates() { return "(" + x + ", " + y + ")"; } public java.awt.geom.Point2D.Double toPoint2D() { return new java.awt.geom.Point2D.Double(x,y); } public Object clone() { try { return super.clone(); } catch(CloneNotSupportedException e) { return null; // never happens } } // identical to Double2D public final int hashCode() { double x = this.x; double y = this.y; // push -0.0 to 0.0 for purposes of hashing. Note that equals() has also been modified // to consider -0.0 to be equal to 0.0. Hopefully cute Java compilers won't try to optimize this out. if (x == -0.0) x = 0.0; if (y == -0.0) y = 0.0; // so we hash to the same value as Int2D does, if we're ints. if ((((int)x) == x) && ((int)y) == y) //return Int2D.hashCodeFor((int)x,(int)y); { int y_ = (int)y; int x_ = (int)x; // copied from Int2D and inserted here because hashCodeFor can't be // inlined and this saves us a fair chunk on some hash-heavy applications y_ += ~(y_ << 15); y_ ^= (y_ >>> 10); y_ += (y_ << 3); y_ ^= (y_ >>> 6); y_ += ~(y_ << 11); y_ ^= (y_ >>> 16); // nifty! Now mix in x return x_ ^ y_; } // I don't like Sun's simplistic approach to random shuffling. So... // basically we need to randomly disperse <double,double> --> int // We do this by doing <double,double> -> <long,long> -> long -> int // The first step is done with doubleToLongBits (not RawLongBits; // we want all NaN to hash to the same thing). Then conversion to // a single long is done by hashing (shuffling) y, then xoring it with x. // So I need something that will hash y to a nicely random value. // this taken from http://www.cris.com/~Ttwang/tech/inthash.htm // Last we fold the long onto itself to form the int. // Some further discussion. Sun's moved to a new hash table scheme // which has (of all things!) tables with lengths that are powers of two! // Normally hash table lengths should be prime numbers, in order to // compensate for bad hashcodes. To fix matters, Sun now is // pre-shuffling the hashcodes with the following algorithm (which // is short but not too bad -- should we adopt it? Dunno). See // http://developer.java.sun.com/developer/bugParade/bugs/4669519.html // key += ~(key << 9); // key ^= (key >>> 14); // key += (key << 4); // key ^= (key >>> 10); // This is good for us because Int2D, Int3D, MutableDouble2D, and Double3D // have hashcodes well distributed with regard to y and z, but when // you mix in x, they're just linear in x. We could do a final // shuffle I guess. In Java 1.3, they DON'T do a pre-shuffle, so // it may be suboptimal. Since we're all moving to 1.4.x, it's not // a big deal since 1.4.x is shuffling the final result using the // Sun shuffler above. But I'd appreciate some tests on our method // below, and suggestions as to whether or not we should adopt the // shorter, likely suboptimal but faster Sun shuffler instead // for y and z values. -- Sean long key = Double.doubleToLongBits(y); key += ~(key << 32); key ^= (key >>> 22); key += ~(key << 13); key ^= (key >>> 8); key += (key << 3); key ^= (key >>> 15); key += ~(key << 27); key ^= (key >>> 31); // nifty! Now mix in x key ^= Double.doubleToLongBits(x); // Last we fold on top of each other return (int)(key ^ (key >> 32)); } // can't have separate equals(...) methods as the // argument isn't virtual public final boolean equals(final Object obj) { if (obj==null) return false; else if (obj instanceof Double2D) // do Double2D first { Double2D other = (Double2D) obj; // Note: commented out because it can't handle 0.0 == -0.0, grrr return ((x == other.x || (Double.isNaN(x) && Double.isNaN(other.x))) && // they're the same or they're both NaN (y == other.y || (Double.isNaN(y) && Double.isNaN(other.y)))); // they're the same or they're both NaN // can't just do other.x == x && other.y == y because we need to check for NaN // return (Double.doubleToLongBits(other.x) == Double.doubleToLongBits(x) && // Double.doubleToLongBits(other.y) == Double.doubleToLongBits(y)); } if (obj instanceof MutableDouble2D) { MutableDouble2D other = (MutableDouble2D) obj; // Note: commented out because it can't handle 0.0 == -0.0, grrr return ((x == other.x || (Double.isNaN(x) && Double.isNaN(other.x))) && // they're the same or they're both NaN (y == other.y || (Double.isNaN(y) && Double.isNaN(other.y)))); // they're the same or they're both NaN // can't just do other.x == x && other.y == y because we need to check for NaN // return (Double.doubleToLongBits(other.x) == Double.doubleToLongBits(x) && // Double.doubleToLongBits(other.y) == Double.doubleToLongBits(y)); } else if (obj instanceof Int2D) { Int2D other = (Int2D) obj; return (other.x == x && other.y == y); } else if (obj instanceof MutableInt2D) { MutableInt2D other = (MutableInt2D) obj; return (other.x == x && other.y == y); } else return false; } /** Returns the distance FROM this MutableDouble2D TO the specified point */ public double distance(final double x, final double y) { final double dx = (double)this.x - x; final double dy = (double)this.y - y; return Math.sqrt(dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distance(final MutableDouble2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return Math.sqrt(dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distance(final Double2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return Math.sqrt(dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distance(final Int2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return Math.sqrt(dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distance(final MutableInt2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return Math.sqrt(dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distance(final java.awt.geom.Point2D p) { final double dx = (double)this.x - p.getX(); final double dy = (double)this.y - p.getY(); return Math.sqrt(dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point */ public double distanceSq(final double x, final double y) { final double dx = (double)this.x - x; final double dy = (double)this.y - y; return (dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distanceSq(final Double2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return (dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distanceSq(final MutableDouble2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return (dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distanceSq(final Int2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return (dx*dx+dy*dy); } /** Returns the distance FROM this MutableDouble2D TO the specified point. */ public double distanceSq(final MutableInt2D p) { final double dx = (double)this.x - p.x; final double dy = (double)this.y - p.y; return (dx*dx+dy*dy); } /** Returns the distance FROM this Point2D TO the specified point */ public double distanceSq(final java.awt.geom.Point2D p) { final double dx = (double)this.x - p.getX(); final double dy = (double)this.y - p.getY(); return (dx*dx+dy*dy); } /** Returns the manhtattan distance FROM this MuableDouble2D TO the specified point */ public double manhattanDistance(final double x, final double y) { final double dx = Math.abs((double)this.x - x); final double dy = Math.abs((double)this.y - y); return dx + dy; } /** Returns the manhtattan distance FROM this MuableDouble2D TO the specified point */ public double manhattanDistance(final Double2D p) { final double dx = Math.abs((double)this.x - p.x); final double dy = Math.abs((double)this.y - p.y); return dx + dy; } /** Returns the manhtattan distance FROM this MuableDouble2D TO the specified point */ public double manhattanDistance(final Int2D p) { final double dx = Math.abs((double)this.x - p.x); final double dy = Math.abs((double)this.y - p.y); return dx + dy; } /** Returns the manhtattan distance FROM this MuableDouble2D TO the specified point */ public double manhattanDistance(final MutableDouble2D p) { final double dx = Math.abs((double)this.x - p.x); final double dy = Math.abs((double)this.y - p.y); return dx + dy; } /** Returns the manhtattan distance FROM this MuableDouble2D TO the specified point */ public double manhattanDistance(final MutableInt2D p) { final double dx = Math.abs((double)this.x - p.x); final double dy = Math.abs((double)this.y - p.y); return dx + dy; } /** Returns the manhtattan distance FROM this MuableDouble2D TO the specified point */ public double manhattanDistance(final java.awt.geom.Point2D p) { final double dx = Math.abs((double)this.x - p.getX()); final double dy = Math.abs((double)this.y - p.getY()); return dx + dy; } /** Adds other into me, returning me. */ public final MutableDouble2D addIn(final Double2D other) { x = other.x + x; y = other.y + y; return this; } /** Adds other into me, returning me. */ public final MutableDouble2D addIn(final MutableDouble2D other) { x = other.x + x; y = other.y + y; return this; } /** Adds the x and y values into my x and y values, returning me. */ public final MutableDouble2D addIn(final double x, final double y) { this.x += x; this.y += y; return this; } /** Sets me to the sum of other1 and other2, returning me. */ public final MutableDouble2D add(final MutableDouble2D other1, final MutableDouble2D other2) { x = other1.x + other2.x; y = other1.y + other2.y; return this; } /** Sets me to the sum of other1 and other2, returning me. */ public final MutableDouble2D add(final Double2D other1, final MutableDouble2D other2) { x = other1.x + other2.x; y = other1.y + other2.y; return this; } /** Sets me to the sum of other1 and other2, returning me. */ public final MutableDouble2D add(final MutableDouble2D other1, final Double2D other2) { x = other1.x + other2.x; y = other1.y + other2.y; return this; } /** Equivalent to <tt>(new MutableDouble2D(d))</tt>, but <tt>(d.dup())</tt> shorter of course, but perhaps not quite as fast. */ public final MutableDouble2D dup() { return new MutableDouble2D(this); } /** Sets me to me minus other, returning me. */ public final MutableDouble2D subtractIn(Double2D other) { x = x - other.x; y = y - other.y; return this; } /** Sets me to me minus other, returning me. */ public final MutableDouble2D subtractIn(MutableDouble2D other) { x = x - other.x; y = y - other.y; return this; } /** Subtracts other2 from other1, setting me to the result and returning me. */ public final MutableDouble2D subtract(MutableDouble2D other1, MutableDouble2D other2) { x = other1.x - other2.x; y = other1.y - other2.y; return this; } /** Subtracts other2 from other1, setting me to the result and returning me. */ public final MutableDouble2D subtract(Double2D other1, MutableDouble2D other2) { x = other1.x - other2.x; y = other1.y - other2.y; return this; } /** Subtracts other2 from other1, setting me to the result and returning me. */ public final MutableDouble2D subtract(MutableDouble2D other1, Double2D other2) { x = other1.x - other2.x; y = other1.y - other2.y; return this; } /** Returns the length of the vector. */ public final double length() { return Math.sqrt(x * x + y * y); } /** Returns the square of the length of the MutableDouble2D. */ public final double lengthSq() { return x*x+y*y; } /** Returns the length of the vector between -Pi and Pi. */ public final double angle() { return Math.atan2(y,x); } /** Extends my length so that it is multiplied by val, and returns me. */ public final MutableDouble2D multiplyIn(final double val) { x = x * val; y = y * val; return this; } /** Multiplies other by val, setting me to the result and returning me. */ public final MutableDouble2D multiply(MutableDouble2D other, final double val) { x = other.x * val; y = other.y * val; return this; } /** Multiplies other by val, setting me to the result and returning me. */ public final MutableDouble2D multiply(Double2D other, final double val) { x = other.x * val; y = other.y * val; return this; } /** Normalizes me (sets my length to 1.0), returning me. Throws an error if my previous length was of length 0. */ static final double infinity = 1.0 / 0.0; public final MutableDouble2D normalize() { final double invertedlen = 1.0 / Math.sqrt(x * x + y * y); if (invertedlen == infinity || invertedlen == -infinity || invertedlen == 0 || invertedlen != invertedlen /* nan */) throw new ArithmeticException("" + this + " length is " + Math.sqrt(x * x + y * y) + ", cannot normalize"); x = x * invertedlen; y = y * invertedlen; return this; } /** Sets my length, which should be >= 0. @deprecated use resize instead [renaming] */ public final MutableDouble2D setLength(double val) { return resize(val); } /** Sets my length, which should be >= 0. */ public final MutableDouble2D resize(double val) { if (val < 0) throw new IllegalArgumentException("The argument to MutableDouble2D.setLength(...) must be zero or positive"); if (val == 0) x = y = 0; else { final double len = Math.sqrt(x * x + y * y); if (len != len || len == infinity || len == -infinity || len == 0) throw new ArithmeticException("" + this + " length is "+ len + " cannot change its length"); final double invertedlen = val / len; x = x * invertedlen; y = y * invertedlen; } return this; } /** Rotates me by theta radians, returning me. */ public final MutableDouble2D rotate(double theta) { // Do the equivalent of multiplying by a 2D rotation // matrix without the overhead of converting the Double2D into // a matrix final double sinTheta = Math.sin(theta); final double cosTheta = Math.cos(theta); final double x = this.x; final double y = this.y; this.x = cosTheta * x + -sinTheta * y; this.y = sinTheta * x + cosTheta * y; return this; } /** Returns the dot product of myself against other, that is me DOT other. */ public final double dot(MutableDouble2D other) { return other.x * x + other.y * y; } /** 2D version of the cross product: returns the dot product of me rotated 90 degrees dotted against the other vector. Does not modify either vector. */ public double perpDot(MutableDouble2D other) { return (-this.y) * other.x + this.x * other.y; } /** Sets the values to 0 and returns it. */ public final MutableDouble2D zero() { this.x = 0; this.y = 0; return this; } /** Sets the values to the negation of the values in the provided MutableDouble2D */ public final MutableDouble2D setToMinus(final MutableDouble2D b) { x = -b.x; y = -b.y; return this; } /** Negates the MutableDouble2D's values and returns it. */ public final MutableDouble2D negate() { x = -x; y = -y; return this; } }