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 Serializable {
16 private static final LogHelper log = Application.getLogger();
18 //////// Debug section
20 * Debugging info. If openrocket.debug.coordinatecount is defined, a line is
21 * printed every 1000000 instantiations (or as many as defined).
23 private static final boolean COUNT_DEBUG;
24 private static final int COUNT_DIFF;
26 String str = System.getProperty("openrocket.debug.coordinatecount");
34 diff = Integer.parseInt(str);
35 } catch (NumberFormatException ignore) {
43 private static int count = 0;
47 synchronized (Coordinate.class) {
49 if ((count % COUNT_DIFF) == 0) {
50 log.debug("Coordinate instantiated " + count + " times.");
56 //////// End debug section
60 public static final Coordinate NUL = new Coordinate(0, 0, 0, 0);
61 public static final Coordinate NaN = new Coordinate(Double.NaN, Double.NaN,
62 Double.NaN, Double.NaN);
64 public final double x, y, z;
65 public final double weight;
68 private double length = -1; /* Cached when calculated */
77 public Coordinate(double x) {
81 public Coordinate(double x, double y) {
85 public Coordinate(double x, double y, double z) {
89 public Coordinate(double x, double y, double z, double w) {
98 public boolean isWeighted() {
103 * Check whether any of the coordinate values is NaN.
105 * @return true if the x, y, z or weight is NaN
107 public boolean isNaN() {
108 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
111 public Coordinate setX(double x) {
112 return new Coordinate(x, this.y, this.z, this.weight);
115 public Coordinate setY(double y) {
116 return new Coordinate(this.x, y, this.z, this.weight);
119 public Coordinate setZ(double z) {
120 return new Coordinate(this.x, this.y, z, this.weight);
123 public Coordinate setWeight(double weight) {
124 return new Coordinate(this.x, this.y, this.z, weight);
127 public Coordinate setXYZ(Coordinate c) {
128 return new Coordinate(c.x, c.y, c.z, this.weight);
133 * Add the coordinate and weight of two coordinates.
135 * @param other the other <code>Coordinate</code>
136 * @return the sum of the coordinates
138 public Coordinate add(Coordinate other) {
139 return new Coordinate(this.x + other.x, this.y + other.y, this.z + other.z,
140 this.weight + other.weight);
143 public Coordinate add(double x, double y, double z) {
144 return new Coordinate(this.x + x, this.y + y, this.z + z, this.weight);
147 public Coordinate add(double x, double y, double z, double weight) {
148 return new Coordinate(this.x + x, this.y + y, this.z + z, this.weight + weight);
152 * Subtract a Coordinate from this Coordinate. The weight of the resulting Coordinate
153 * is the same as of this Coordinate, the weight of the argument is ignored.
155 * @param other Coordinate to subtract from this.
158 public Coordinate sub(Coordinate other) {
159 return new Coordinate(this.x - other.x, this.y - other.y, this.z - other.z, this.weight);
163 * Subtract the specified values from this Coordinate. The weight of the result
164 * is the same as the weight of this Coordinate.
166 * @param x x value to subtract
167 * @param y y value to subtract
168 * @param z z value to subtract
169 * @return the result.
171 public Coordinate sub(double x, double y, double z) {
172 return new Coordinate(this.x - x, this.y - y, this.z - z, this.weight);
177 * Multiply the <code>Coordinate</code> with a scalar. All coordinates and the
178 * weight are multiplied by the given scalar.
180 * @param m Factor to multiply by.
181 * @return The product.
183 public Coordinate multiply(double m) {
184 return new Coordinate(this.x * m, this.y * m, this.z * m, this.weight * m);
188 * Dot product of two Coordinates, taken as vectors. Equal to
190 * @param other Coordinate to take product with.
191 * @return The dot product.
193 public double dot(Coordinate other) {
194 return this.x * other.x + this.y * other.y + this.z * other.z;
198 * Dot product of two Coordinates.
200 public static double dot(Coordinate v1, Coordinate v2) {
201 return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
205 * Distance from the origin to the Coordinate.
207 public double length() {
209 length = MathUtil.safeSqrt(x * x + y * y + z * z);
215 * Square of the distance from the origin to the Coordinate.
217 public double length2() {
218 return x * x + y * y + z * z;
223 * Return the largest of the absolute values of the coordinates. This can be
224 * used as a norm of the vector that is faster to calculate than the
227 * @return the largest absolute value of (x,y,z)
229 public double max() {
230 return MathUtil.max(Math.abs(x), Math.abs(y), Math.abs(z));
235 * Returns a new coordinate which has the same direction from the origin as this
236 * coordinate but is at a distance of one. If this coordinate is the origin,
237 * this method throws an <code>IllegalStateException</code>. The weight of the
238 * coordinate is unchanged.
240 * @return the coordinate normalized to distance one of the origin.
241 * @throws IllegalStateException if this coordinate is the origin.
243 public Coordinate normalize() {
246 throw new IllegalStateException("Cannot normalize zero coordinate");
248 return new Coordinate(x / l, y / l, z / l, weight);
255 * Weighted average of two coordinates. If either of the weights are positive,
256 * the result is the weighted average of the coordinates and the weight is the sum
257 * of the original weights. If the sum of the weights is zero (and especially if
258 * both of the weights are zero), the result is the unweighted average of the
259 * coordinates with weight zero.
261 * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
264 public Coordinate average(Coordinate other) {
270 w = this.weight + other.weight;
271 if (Math.abs(w) < MathUtil.pow2(MathUtil.EPSILON)) {
272 x = (this.x + other.x) / 2;
273 y = (this.y + other.y) / 2;
274 z = (this.z + other.z) / 2;
277 x = (this.x * this.weight + other.x * other.weight) / w;
278 y = (this.y * this.weight + other.y * other.weight) / w;
279 z = (this.z * this.weight + other.z * other.weight) / w;
281 return new Coordinate(x, y, z, w);
286 * Tests whether the coordinates are the equal.
288 * @param other Coordinate to compare to.
289 * @return true if the coordinates are equal (x, y, z and weight)
292 public boolean equals(Object other) {
293 if (!(other instanceof Coordinate))
296 final Coordinate c = (Coordinate) other;
297 return (MathUtil.equals(this.x, c.x) &&
298 MathUtil.equals(this.y, c.y) &&
299 MathUtil.equals(this.z, c.z) && MathUtil.equals(this.weight, c.weight));
303 * Hash code method compatible with {@link #equals(Object)}.
306 public int hashCode() {
307 return (int) ((x + y + z) * 100000);
312 public String toString() {
314 return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x, y, z, weight);
316 return String.format("(%.3f,%.3f,%.3f)", x, y, z);