1 package net.sf.openrocket.util;
3 import java.io.Serializable;
5 import net.sf.openrocket.logging.LogHelper;
6 import net.sf.openrocket.startup.Application;
9 * An immutable class of weighted coordinates. The weights are non-negative.
11 * Can also be used as non-weighted coordinates with weight=0.
13 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
15 public final class Coordinate implements Cloneable, Serializable {
16 private static final LogHelper log = Application.getLogger();
18 // Defined for backwards compatibility after adding clone().
19 static final long serialVersionUID = 585574649794259293L;
21 //////// Debug section
23 * Debugging info. If openrocket.debug.coordinatecount is defined, a line is
24 * printed every 1000000 instantiations (or as many as defined).
26 private static final boolean COUNT_DEBUG;
27 private static final int COUNT_DIFF;
29 String str = System.getProperty("openrocket.debug.coordinatecount");
37 diff = Integer.parseInt(str);
38 } catch (NumberFormatException ignore) {
46 private static int count = 0;
50 synchronized (Coordinate.class) {
52 if ((count % COUNT_DIFF) == 0) {
53 log.debug("Coordinate instantiated " + count + " times.");
59 //////// End debug section
63 public static final Coordinate NUL = new Coordinate(0, 0, 0, 0);
64 public static final Coordinate NaN = new Coordinate(Double.NaN, Double.NaN,
65 Double.NaN, Double.NaN);
67 public final double x, y, z;
68 public final double weight;
71 private double length = -1; /* Cached when calculated */
78 public Coordinate(double x) {
82 public Coordinate(double x, double y) {
86 public Coordinate(double x, double y, double z) {
90 public Coordinate(double x, double y, double z, double w) {
99 public boolean isWeighted() {
100 return (weight != 0);
104 * Check whether any of the coordinate values is NaN.
106 * @return true if the x, y, z or weight is NaN
108 public boolean isNaN() {
109 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
112 public Coordinate setX(double x) {
113 return new Coordinate(x, this.y, this.z, this.weight);
116 public Coordinate setY(double y) {
117 return new Coordinate(this.x, y, this.z, this.weight);
120 public Coordinate setZ(double z) {
121 return new Coordinate(this.x, this.y, z, this.weight);
124 public Coordinate setWeight(double weight) {
125 return new Coordinate(this.x, this.y, this.z, weight);
128 public Coordinate setXYZ(Coordinate c) {
129 return new Coordinate(c.x, c.y, c.z, this.weight);
134 * Add the coordinate and weight of two coordinates.
136 * @param other the other <code>Coordinate</code>
137 * @return the sum of the coordinates
139 public Coordinate add(Coordinate other) {
140 return new Coordinate(this.x + other.x, this.y + other.y, this.z + other.z,
141 this.weight + other.weight);
144 public Coordinate add(double x, double y, double z) {
145 return new Coordinate(this.x + x, this.y + y, this.z + z, this.weight);
148 public Coordinate add(double x, double y, double z, double weight) {
149 return new Coordinate(this.x + x, this.y + y, this.z + z, this.weight + weight);
153 * Subtract a Coordinate from this Coordinate. The weight of the resulting Coordinate
154 * is the same as of this Coordinate, the weight of the argument is ignored.
156 * @param other Coordinate to subtract from this.
159 public Coordinate sub(Coordinate other) {
160 return new Coordinate(this.x - other.x, this.y - other.y, this.z - other.z, this.weight);
164 * Subtract the specified values from this Coordinate. The weight of the result
165 * is the same as the weight of this Coordinate.
167 * @param x x value to subtract
168 * @param y y value to subtract
169 * @param z z value to subtract
170 * @return the result.
172 public Coordinate sub(double x, double y, double z) {
173 return new Coordinate(this.x - x, this.y - y, this.z - z, this.weight);
178 * Multiply the <code>Coordinate</code> with a scalar. All coordinates and the
179 * weight are multiplied by the given scalar.
181 * @param m Factor to multiply by.
182 * @return The product.
184 public Coordinate multiply(double m) {
185 return new Coordinate(this.x * m, this.y * m, this.z * m, this.weight * m);
189 * Dot product of two Coordinates, taken as vectors. Equal to
191 * @param other Coordinate to take product with.
192 * @return The dot product.
194 public double dot(Coordinate other) {
195 return this.x * other.x + this.y * other.y + this.z * other.z;
199 * Dot product of two Coordinates.
201 public static double dot(Coordinate v1, Coordinate v2) {
202 return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
206 * Distance from the origin to the Coordinate.
208 public double length() {
210 length = MathUtil.safeSqrt(x * x + y * y + z * z);
216 * Square of the distance from the origin to the Coordinate.
218 public double length2() {
219 return x * x + y * y + z * z;
224 * Return the largest of the absolute values of the coordinates. This can be
225 * used as a norm of the vector that is faster to calculate than the
228 * @return the largest absolute value of (x,y,z)
230 public double max() {
231 return MathUtil.max(Math.abs(x), Math.abs(y), Math.abs(z));
236 * Returns a new coordinate which has the same direction from the origin as this
237 * coordinate but is at a distance of one. If this coordinate is the origin,
238 * this method throws an <code>IllegalStateException</code>. The weight of the
239 * coordinate is unchanged.
241 * @return the coordinate normalized to distance one of the origin.
242 * @throws IllegalStateException if this coordinate is the origin.
244 public Coordinate normalize() {
247 throw new IllegalStateException("Cannot normalize zero coordinate");
249 return new Coordinate(x / l, y / l, z / l, weight);
256 * Weighted average of two coordinates. If either of the weights are positive,
257 * the result is the weighted average of the coordinates and the weight is the sum
258 * of the original weights. If the sum of the weights is zero (and especially if
259 * both of the weights are zero), the result is the unweighted average of the
260 * coordinates with weight zero.
262 * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
265 public Coordinate average(Coordinate other) {
271 w = this.weight + other.weight;
272 if (Math.abs(w) < MathUtil.pow2(MathUtil.EPSILON)) {
273 x = (this.x + other.x) / 2;
274 y = (this.y + other.y) / 2;
275 z = (this.z + other.z) / 2;
278 x = (this.x * this.weight + other.x * other.weight) / w;
279 y = (this.y * this.weight + other.y * other.weight) / w;
280 z = (this.z * this.weight + other.z * other.weight) / w;
282 return new Coordinate(x, y, z, w);
287 * Tests whether the coordinates are the equal.
289 * @param other Coordinate to compare to.
290 * @return true if the coordinates are equal (x, y, z and weight)
293 public boolean equals(Object other) {
294 if (!(other instanceof Coordinate))
297 final Coordinate c = (Coordinate) other;
298 return (MathUtil.equals(this.x, c.x) &&
299 MathUtil.equals(this.y, c.y) &&
300 MathUtil.equals(this.z, c.z) && MathUtil.equals(this.weight, c.weight));
304 * Hash code method compatible with {@link #equals(Object)}.
307 public int hashCode() {
308 return (int) ((x + y + z) * 100000);
313 public String toString() {
315 return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x, y, z, weight);
317 return String.format("(%.3f,%.3f,%.3f)", x, y, z);
321 public Coordinate clone() {
322 return new Coordinate( this.x, this.y, this.z, this.weight );