5b88f08a694211a44bf8edcdc10d3e8c2037ff98
[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         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;
19         
20         
21         private double length = -1;  /* Cached when calculated */
22         
23         
24         /* Count and report the number of times a Coordinate is constructed: */
25 //      private static int count=0;
26 //      {
27 //              count++;
28 //              if ((count % 1000) == 0) {
29 //                      System.err.println("Coordinate instantiated "+count+" times");
30 //              }
31 //      }
32         
33         
34
35         public Coordinate() {
36                 this(0,0,0,0);
37         }
38         
39         public Coordinate(double x) {
40                 this(x,0,0,0);
41         }
42         
43         public Coordinate(double x, double y) {
44                 this(x,y,0,0);
45         }
46         
47         public Coordinate(double x, double y, double z) {
48                 this(x,y,z,0);
49         }
50         public Coordinate(double x, double y, double z, double w) {
51                 this.x = x;
52                 this.y = y;
53                 this.z = z;
54                 this.weight=w;
55         }
56
57         
58         public boolean isWeighted() {
59                 return (weight != 0);
60         }
61         
62         public boolean isNaN() {
63                 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
64         }
65         
66         public Coordinate setX(double x) {
67                 return new Coordinate(x,this.y,this.z,this.weight);
68         }
69         
70         public Coordinate setY(double y) {
71                 return new Coordinate(this.x,y,this.z,this.weight);
72         }
73         
74         public Coordinate setZ(double z) {
75                 return new Coordinate(this.x,this.y,z,this.weight);
76         }
77         
78         public Coordinate setWeight(double weight) {
79                 return new Coordinate(this.x, this.y, this.z, weight);
80         }
81         
82         public Coordinate setXYZ(Coordinate c) {
83                 return new Coordinate(c.x, c.y, c.z, this.weight);
84         }
85
86         
87         /**
88          * Add the coordinate and weight of two coordinates.
89          * 
90          * @param other  the other <code>Coordinate</code>
91          * @return               the sum of the coordinates
92          */
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);
96         }
97         
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);
100         }
101
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);
104         }
105
106         /**
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.
109          * 
110          * @param other  Coordinate to subtract from this.
111          * @return  The result
112          */
113         public Coordinate sub(Coordinate other) {
114                 return new Coordinate(this.x-other.x, this.y-other.y, this.z-other.z, this.weight);
115         }
116
117         /**
118          * Subtract the specified values from this Coordinate.  The weight of the result
119          * is the same as the weight of this Coordinate.
120          * 
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.
125          */
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);
128         }
129         
130         
131         /**
132          * Multiply the <code>Coordinate</code> with a scalar.  All coordinates and the
133          * weight are multiplied by the given scalar.
134
135          * @param m  Factor to multiply by.
136          * @return   The product. 
137          */
138         public Coordinate multiply(double m) {
139                 return new Coordinate(this.x*m, this.y*m, this.z*m, this.weight*m);
140         }
141
142         /**
143          * Dot product of two Coordinates, taken as vectors.  Equal to
144          * x1*x2+y1*y2+z1*z2
145          * @param other  Coordinate to take product with.
146          * @return   The dot product.
147          */
148         public double dot(Coordinate other) {
149                 return this.x*other.x + this.y*other.y + this.z*other.z;
150         }
151         /**
152          * Dot product of two Coordinates.
153          */
154         public static double dot(Coordinate v1, Coordinate v2) {
155                 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
156         }
157
158         /**
159          * Distance from the origin to the Coordinate.
160          */
161         public double length() {
162                 if (length < 0) {
163                         length = Math.sqrt(x*x+y*y+z*z); 
164                 }
165                 return length;
166         }
167         
168         /**
169          * Square of the distance from the origin to the Coordinate.
170          */
171         public double length2() {
172                 return x*x+y*y+z*z;
173         }
174         
175         /**
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.
180          * 
181          * @return   the coordinate normalized to distance one of the origin.
182          * @throws   IllegalStateException  if this coordinate is the origin.
183          */
184         public Coordinate normalize() {
185                 double l = length();
186                 if (l < 0.0000001) {
187                         throw new IllegalStateException("Cannot normalize zero coordinate");
188                 }
189                 return new Coordinate(x/l, y/l, z/l, weight);
190         }
191         
192         
193         
194         
195         /**
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.
201          * <p>
202          * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
203          * returned.
204          */
205         public Coordinate average(Coordinate other) {
206                 double x,y,z,w;
207                 
208                 if (other == null)
209                         return this;
210                 
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;
216                         w = 0;
217                 } else {
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;
221                 }
222                 return new Coordinate(x,y,z,w);
223         }
224         
225         
226         /**
227          * Tests whether the coordinates (not weight!) are the same.
228          * 
229          * Compares only the (x,y,z) coordinates, NOT the weight.  Coordinate comparison is
230          * done to the precision of COMPARISON_DELTA.
231          * 
232          * @param other  Coordinate to compare to.
233          * @return  true if the coordinates are equal
234          */
235         @Override
236         public boolean equals(Object other) {
237                 if (!(other instanceof Coordinate))
238                         return false;
239                 
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));
244         }
245         
246         /**
247          * Hash code method compatible with {@link #equals(Object)}.
248          */
249         @Override
250         public int hashCode() {
251                 return (int)((x+y+z)*100000);
252         }
253         
254         
255         @Override
256         public String toString() {
257                 if (isWeighted())
258                         return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
259                 else
260                         return String.format("(%.3f,%.3f,%.3f)", x,y,z);
261         }
262         
263         
264         
265         public static void main(String[] arg) {
266                 double a=1.2;
267                 double x;
268                 Coordinate c;
269                 long t1, t2;
270                 
271                 x = 0;
272                 t1 = System.nanoTime();
273                 for (int i=0; i < 100000000; i++) {
274                         x = x + a;
275                 }
276                 t2 = System.nanoTime();
277                 System.out.println("Value: "+x);
278                 System.out.println("Plain addition: "+ ((t2-t1+500000)/1000000) + " ms");
279                 
280                 c = Coordinate.NUL;
281                 t1 = System.nanoTime();
282                 for (int i=0; i < 100000000; i++) {
283                         c = c.add(a,0,0);
284                 }
285                 t2 = System.nanoTime();
286                 System.out.println("Value: "+c.x);
287                 System.out.println("Coordinate addition: "+ ((t2-t1+500000)/1000000) + " ms");
288                 
289         }
290         
291 }