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 {
13 public static final Coordinate NUL = new Coordinate(0,0,0,0);
14 public static final Coordinate NaN = new Coordinate(Double.NaN,Double.NaN,
15 Double.NaN,Double.NaN);
16 public static final double COMPARISON_DELTA = 0.000001;
17 public final double x,y,z;
18 public final double weight;
21 private double length = -1; /* Cached when calculated */
24 /* Count and report the number of times a Coordinate is constructed: */
25 // private static int count=0;
28 // if ((count % 1000) == 0) {
29 // System.err.println("Coordinate instantiated "+count+" times");
39 public Coordinate(double x) {
43 public Coordinate(double x, double y) {
47 public Coordinate(double x, double y, double z) {
50 public Coordinate(double x, double y, double z, double w) {
58 public boolean isWeighted() {
62 public boolean isNaN() {
63 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
66 public Coordinate setX(double x) {
67 return new Coordinate(x,this.y,this.z,this.weight);
70 public Coordinate setY(double y) {
71 return new Coordinate(this.x,y,this.z,this.weight);
74 public Coordinate setZ(double z) {
75 return new Coordinate(this.x,this.y,z,this.weight);
78 public Coordinate setWeight(double weight) {
79 return new Coordinate(this.x, this.y, this.z, weight);
82 public Coordinate setXYZ(Coordinate c) {
83 return new Coordinate(c.x, c.y, c.z, this.weight);
88 * Add the coordinate and weight of two coordinates.
90 * @param other the other <code>Coordinate</code>
91 * @return the sum of the coordinates
93 public Coordinate add(Coordinate other) {
94 return new Coordinate(this.x+other.x, this.y+other.y, this.z+other.z,
95 this.weight+other.weight);
98 public Coordinate add(double x, double y, double z) {
99 return new Coordinate(this.x+x, this.y+y, this.z+z, this.weight);
102 public Coordinate add(double x, double y, double z, double weight) {
103 return new Coordinate(this.x+x, this.y+y, this.z+z, this.weight+weight);
107 * Subtract a Coordinate from this Coordinate. The weight of the resulting Coordinate
108 * is the same as of this Coordinate, the weight of the argument is ignored.
110 * @param other Coordinate to subtract from this.
113 public Coordinate sub(Coordinate other) {
114 return new Coordinate(this.x-other.x, this.y-other.y, this.z-other.z, this.weight);
118 * Subtract the specified values from this Coordinate. The weight of the result
119 * is the same as the weight of this Coordinate.
121 * @param x x value to subtract
122 * @param y y value to subtract
123 * @param z z value to subtract
124 * @return the result.
126 public Coordinate sub(double x, double y, double z) {
127 return new Coordinate(this.x - x, this.y - y, this.z - z, this.weight);
132 * Multiply the <code>Coordinate</code> with a scalar. All coordinates and the
133 * weight are multiplied by the given scalar.
135 * @param m Factor to multiply by.
136 * @return The product.
138 public Coordinate multiply(double m) {
139 return new Coordinate(this.x*m, this.y*m, this.z*m, this.weight*m);
143 * Dot product of two Coordinates, taken as vectors. Equal to
145 * @param other Coordinate to take product with.
146 * @return The dot product.
148 public double dot(Coordinate other) {
149 return this.x*other.x + this.y*other.y + this.z*other.z;
152 * Dot product of two Coordinates.
154 public static double dot(Coordinate v1, Coordinate v2) {
155 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
159 * Distance from the origin to the Coordinate.
161 public double length() {
163 length = Math.sqrt(x*x+y*y+z*z);
169 * Square of the distance from the origin to the Coordinate.
171 public double length2() {
176 * Returns a new coordinate which has the same direction from the origin as this
177 * coordinate but is at a distance of one. If this coordinate is the origin,
178 * this method throws an <code>IllegalStateException</code>. The weight of the
179 * coordinate is unchanged.
181 * @return the coordinate normalized to distance one of the origin.
182 * @throws IllegalStateException if this coordinate is the origin.
184 public Coordinate normalize() {
187 throw new IllegalStateException("Cannot normalize zero coordinate");
189 return new Coordinate(x/l, y/l, z/l, weight);
196 * Weighted average of two coordinates. If either of the weights are positive,
197 * the result is the weighted average of the coordinates and the weight is the sum
198 * of the original weights. If the sum of the weights is zero (and especially if
199 * both of the weights are zero), the result is the unweighted average of the
200 * coordinates with weight zero.
202 * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
205 public Coordinate average(Coordinate other) {
211 w = this.weight + other.weight;
212 if (Math.abs(w) < MathUtil.pow2(MathUtil.EPSILON)) {
213 x = (this.x+other.x)/2;
214 y = (this.y+other.y)/2;
215 z = (this.z+other.z)/2;
218 x = (this.x*this.weight + other.x*other.weight)/w;
219 y = (this.y*this.weight + other.y*other.weight)/w;
220 z = (this.z*this.weight + other.z*other.weight)/w;
222 return new Coordinate(x,y,z,w);
227 * Tests whether the coordinates (not weight!) are the same.
229 * Compares only the (x,y,z) coordinates, NOT the weight. Coordinate comparison is
230 * done to the precision of COMPARISON_DELTA.
232 * @param other Coordinate to compare to.
233 * @return true if the coordinates are equal
236 public boolean equals(Object other) {
237 if (!(other instanceof Coordinate))
240 final Coordinate c = (Coordinate)other;
241 return (MathUtil.equals(this.x, c.x) &&
242 MathUtil.equals(this.y, c.y) &&
243 MathUtil.equals(this.z, c.z));
247 * Hash code method compatible with {@link #equals(Object)}.
250 public int hashCode() {
251 return (int)((x+y+z)*100000);
256 public String toString() {
258 return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
260 return String.format("(%.3f,%.3f,%.3f)", x,y,z);
265 public static void main(String[] arg) {
272 t1 = System.nanoTime();
273 for (int i=0; i < 100000000; i++) {
276 t2 = System.nanoTime();
277 System.out.println("Value: "+x);
278 System.out.println("Plain addition: "+ ((t2-t1+500000)/1000000) + " ms");
281 t1 = System.nanoTime();
282 for (int i=0; i < 100000000; i++) {
285 t2 = System.nanoTime();
286 System.out.println("Value: "+c.x);
287 System.out.println("Coordinate addition: "+ ((t2-t1+500000)/1000000) + " ms");