1 package net.sf.openrocket.util;
3 import java.io.Serializable;
6 * An immutable class of weighted coordinates. The weights are non-negative.
8 * Can also be used as non-weighted coordinates with weight=0.
10 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
12 public final class Coordinate implements Serializable {
14 //////// Debug section
16 * Debugging info. If openrocket.debug.coordinatecount is defined, a line is
17 * printed every 1000000 instantiations (or as many as defined).
19 private static final boolean COUNT_DEBUG;
20 private static final int COUNT_DIFF;
22 String str = System.getProperty("openrocket.debug.coordinatecount", null);
30 diff = Integer.parseInt(str);
31 } catch (NumberFormatException ignore) { }
38 private static int count = 0;
43 if ((count % COUNT_DIFF) == 0) {
44 System.out.println("Coordinate instantiated " + count + " times.");
49 //////// End debug section
53 public static final Coordinate NUL = new Coordinate(0,0,0,0);
54 public static final Coordinate NaN = new Coordinate(Double.NaN,Double.NaN,
55 Double.NaN,Double.NaN);
57 public final double x,y,z;
58 public final double weight;
61 private double length = -1; /* Cached when calculated */
70 public Coordinate(double x) {
74 public Coordinate(double x, double y) {
78 public Coordinate(double x, double y, double z) {
81 public Coordinate(double x, double y, double z, double w) {
90 public boolean isWeighted() {
94 public boolean isNaN() {
95 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
98 public Coordinate setX(double x) {
99 return new Coordinate(x,this.y,this.z,this.weight);
102 public Coordinate setY(double y) {
103 return new Coordinate(this.x,y,this.z,this.weight);
106 public Coordinate setZ(double z) {
107 return new Coordinate(this.x,this.y,z,this.weight);
110 public Coordinate setWeight(double weight) {
111 return new Coordinate(this.x, this.y, this.z, weight);
114 public Coordinate setXYZ(Coordinate c) {
115 return new Coordinate(c.x, c.y, c.z, this.weight);
120 * Add the coordinate and weight of two coordinates.
122 * @param other the other <code>Coordinate</code>
123 * @return the sum of the coordinates
125 public Coordinate add(Coordinate other) {
126 return new Coordinate(this.x+other.x, this.y+other.y, this.z+other.z,
127 this.weight+other.weight);
130 public Coordinate add(double x, double y, double z) {
131 return new Coordinate(this.x+x, this.y+y, this.z+z, this.weight);
134 public Coordinate add(double x, double y, double z, double weight) {
135 return new Coordinate(this.x+x, this.y+y, this.z+z, this.weight+weight);
139 * Subtract a Coordinate from this Coordinate. The weight of the resulting Coordinate
140 * is the same as of this Coordinate, the weight of the argument is ignored.
142 * @param other Coordinate to subtract from this.
145 public Coordinate sub(Coordinate other) {
146 return new Coordinate(this.x-other.x, this.y-other.y, this.z-other.z, this.weight);
150 * Subtract the specified values from this Coordinate. The weight of the result
151 * is the same as the weight of this Coordinate.
153 * @param x x value to subtract
154 * @param y y value to subtract
155 * @param z z value to subtract
156 * @return the result.
158 public Coordinate sub(double x, double y, double z) {
159 return new Coordinate(this.x - x, this.y - y, this.z - z, this.weight);
164 * Multiply the <code>Coordinate</code> with a scalar. All coordinates and the
165 * weight are multiplied by the given scalar.
167 * @param m Factor to multiply by.
168 * @return The product.
170 public Coordinate multiply(double m) {
171 return new Coordinate(this.x*m, this.y*m, this.z*m, this.weight*m);
175 * Dot product of two Coordinates, taken as vectors. Equal to
177 * @param other Coordinate to take product with.
178 * @return The dot product.
180 public double dot(Coordinate other) {
181 return this.x*other.x + this.y*other.y + this.z*other.z;
184 * Dot product of two Coordinates.
186 public static double dot(Coordinate v1, Coordinate v2) {
187 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
191 * Distance from the origin to the Coordinate.
193 public double length() {
195 length = Math.sqrt(x*x+y*y+z*z);
201 * Square of the distance from the origin to the Coordinate.
203 public double length2() {
209 * Return the largest of the absolute values of the coordinates. This can be
210 * used as a norm of the vector that is faster to calculate than the
213 * @return the largest absolute value of (x,y,z)
215 public double max() {
216 return MathUtil.max(Math.abs(x), Math.abs(y), Math.abs(z));
221 * Returns a new coordinate which has the same direction from the origin as this
222 * coordinate but is at a distance of one. If this coordinate is the origin,
223 * this method throws an <code>IllegalStateException</code>. The weight of the
224 * coordinate is unchanged.
226 * @return the coordinate normalized to distance one of the origin.
227 * @throws IllegalStateException if this coordinate is the origin.
229 public Coordinate normalize() {
232 throw new IllegalStateException("Cannot normalize zero coordinate");
234 return new Coordinate(x/l, y/l, z/l, weight);
241 * Weighted average of two coordinates. If either of the weights are positive,
242 * the result is the weighted average of the coordinates and the weight is the sum
243 * of the original weights. If the sum of the weights is zero (and especially if
244 * both of the weights are zero), the result is the unweighted average of the
245 * coordinates with weight zero.
247 * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
250 public Coordinate average(Coordinate other) {
256 w = this.weight + other.weight;
257 if (Math.abs(w) < MathUtil.pow2(MathUtil.EPSILON)) {
258 x = (this.x+other.x)/2;
259 y = (this.y+other.y)/2;
260 z = (this.z+other.z)/2;
263 x = (this.x*this.weight + other.x*other.weight)/w;
264 y = (this.y*this.weight + other.y*other.weight)/w;
265 z = (this.z*this.weight + other.z*other.weight)/w;
267 return new Coordinate(x,y,z,w);
272 * Tests whether the coordinates (not weight!) are the same.
274 * Compares only the (x,y,z) coordinates, NOT the weight. Coordinate comparison is
275 * done to the precision of COMPARISON_DELTA.
277 * @param other Coordinate to compare to.
278 * @return true if the coordinates are equal
281 public boolean equals(Object other) {
282 if (!(other instanceof Coordinate))
285 final Coordinate c = (Coordinate)other;
286 return (MathUtil.equals(this.x, c.x) &&
287 MathUtil.equals(this.y, c.y) &&
288 MathUtil.equals(this.z, c.z));
292 * Hash code method compatible with {@link #equals(Object)}.
295 public int hashCode() {
296 return (int)((x+y+z)*100000);
301 public String toString() {
303 return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
305 return String.format("(%.3f,%.3f,%.3f)", x,y,z);