release 0.9.6
[debian/openrocket] / src / net / sf / openrocket / util / Coordinate.java
1 package net.sf.openrocket.util;
2
3 import java.io.Serializable;
4
5 /**
6  * An immutable class of weighted coordinates.  The weights are non-negative.
7  * 
8  * Can also be used as non-weighted coordinates with weight=0.
9  * 
10  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
11  */
12 public final class Coordinate implements Serializable {
13         
14         ////////  Debug section
15         /*
16          * Debugging info.  If openrocket.debug.coordinatecount is defined, a line is
17          * printed every 1000000 instantiations (or as many as defined).
18          */
19         private static final boolean COUNT_DEBUG;
20         private static final int COUNT_DIFF;
21         static {
22                 String str = System.getProperty("openrocket.debug.coordinatecount", null);
23                 int diff = 0;
24                 if (str == null) {
25                         COUNT_DEBUG = false;
26                         COUNT_DIFF = 0;
27                 } else {
28                         COUNT_DEBUG = true;
29                         try {
30                                 diff = Integer.parseInt(str);
31                         } catch (NumberFormatException ignore) { }
32                         if (diff < 1000)
33                                 diff = 1000000;
34                         COUNT_DIFF = diff;
35                 }
36         }
37         
38         private static int count = 0;
39         {
40                 // Debug count
41                 if (COUNT_DEBUG) {
42                         count++;
43                         if ((count % COUNT_DIFF) == 0) {
44                                 System.out.println("Coordinate instantiated " + count + " times.");
45                         }
46                 }
47         }
48         
49         ////////  End debug section
50         
51         
52         
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);
56
57         public final double x,y,z;
58         public final double weight;
59         
60         
61         private double length = -1;  /* Cached when calculated */
62         
63         
64         
65
66         public Coordinate() {
67                 this(0,0,0,0);
68         }
69         
70         public Coordinate(double x) {
71                 this(x,0,0,0);
72         }
73         
74         public Coordinate(double x, double y) {
75                 this(x,y,0,0);
76         }
77         
78         public Coordinate(double x, double y, double z) {
79                 this(x,y,z,0);
80         }
81         public Coordinate(double x, double y, double z, double w) {
82                 this.x = x;
83                 this.y = y;
84                 this.z = z;
85                 this.weight=w;
86                 
87         }
88
89         
90         public boolean isWeighted() {
91                 return (weight != 0);
92         }
93         
94         public boolean isNaN() {
95                 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
96         }
97         
98         public Coordinate setX(double x) {
99                 return new Coordinate(x,this.y,this.z,this.weight);
100         }
101         
102         public Coordinate setY(double y) {
103                 return new Coordinate(this.x,y,this.z,this.weight);
104         }
105         
106         public Coordinate setZ(double z) {
107                 return new Coordinate(this.x,this.y,z,this.weight);
108         }
109         
110         public Coordinate setWeight(double weight) {
111                 return new Coordinate(this.x, this.y, this.z, weight);
112         }
113         
114         public Coordinate setXYZ(Coordinate c) {
115                 return new Coordinate(c.x, c.y, c.z, this.weight);
116         }
117
118         
119         /**
120          * Add the coordinate and weight of two coordinates.
121          * 
122          * @param other  the other <code>Coordinate</code>
123          * @return               the sum of the coordinates
124          */
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);
128         }
129         
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);
132         }
133
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);
136         }
137
138         /**
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.
141          * 
142          * @param other  Coordinate to subtract from this.
143          * @return  The result
144          */
145         public Coordinate sub(Coordinate other) {
146                 return new Coordinate(this.x-other.x, this.y-other.y, this.z-other.z, this.weight);
147         }
148
149         /**
150          * Subtract the specified values from this Coordinate.  The weight of the result
151          * is the same as the weight of this Coordinate.
152          * 
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.
157          */
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);
160         }
161         
162         
163         /**
164          * Multiply the <code>Coordinate</code> with a scalar.  All coordinates and the
165          * weight are multiplied by the given scalar.
166
167          * @param m  Factor to multiply by.
168          * @return   The product. 
169          */
170         public Coordinate multiply(double m) {
171                 return new Coordinate(this.x*m, this.y*m, this.z*m, this.weight*m);
172         }
173
174         /**
175          * Dot product of two Coordinates, taken as vectors.  Equal to
176          * x1*x2+y1*y2+z1*z2
177          * @param other  Coordinate to take product with.
178          * @return   The dot product.
179          */
180         public double dot(Coordinate other) {
181                 return this.x*other.x + this.y*other.y + this.z*other.z;
182         }
183         /**
184          * Dot product of two Coordinates.
185          */
186         public static double dot(Coordinate v1, Coordinate v2) {
187                 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
188         }
189
190         /**
191          * Distance from the origin to the Coordinate.
192          */
193         public double length() {
194                 if (length < 0) {
195                         length = Math.sqrt(x*x+y*y+z*z); 
196                 }
197                 return length;
198         }
199         
200         /**
201          * Square of the distance from the origin to the Coordinate.
202          */
203         public double length2() {
204                 return x*x+y*y+z*z;
205         }
206         
207         
208         /**
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
211          * 2-norm.
212          * 
213          * @return      the largest absolute value of (x,y,z)
214          */
215         public double max() {
216                 return MathUtil.max(Math.abs(x), Math.abs(y), Math.abs(z));
217         }
218         
219         
220         /**
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.
225          * 
226          * @return   the coordinate normalized to distance one of the origin.
227          * @throws   IllegalStateException  if this coordinate is the origin.
228          */
229         public Coordinate normalize() {
230                 double l = length();
231                 if (l < 0.0000001) {
232                         throw new IllegalStateException("Cannot normalize zero coordinate");
233                 }
234                 return new Coordinate(x/l, y/l, z/l, weight);
235         }
236         
237         
238         
239         
240         /**
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.
246          * <p>
247          * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
248          * returned.
249          */
250         public Coordinate average(Coordinate other) {
251                 double x,y,z,w;
252                 
253                 if (other == null)
254                         return this;
255                 
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;
261                         w = 0;
262                 } else {
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;
266                 }
267                 return new Coordinate(x,y,z,w);
268         }
269         
270         
271         /**
272          * Tests whether the coordinates (not weight!) are the same.
273          * 
274          * Compares only the (x,y,z) coordinates, NOT the weight.  Coordinate comparison is
275          * done to the precision of COMPARISON_DELTA.
276          * 
277          * @param other  Coordinate to compare to.
278          * @return  true if the coordinates are equal
279          */
280         @Override
281         public boolean equals(Object other) {
282                 if (!(other instanceof Coordinate))
283                         return false;
284                 
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));
289         }
290         
291         /**
292          * Hash code method compatible with {@link #equals(Object)}.
293          */
294         @Override
295         public int hashCode() {
296                 return (int)((x+y+z)*100000);
297         }
298         
299         
300         @Override
301         public String toString() {
302                 if (isWeighted())
303                         return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
304                 else
305                         return String.format("(%.3f,%.3f,%.3f)", x,y,z);
306         }
307         
308         
309 }