Initial commit
[debian/openrocket] / src / net / sf / openrocket / util / MutableCoordinate.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 MutableCoordinate implements Serializable {
13         public static final MutableCoordinate NUL = new MutableCoordinate(0,0,0,0);
14         public static final MutableCoordinate NaN = new MutableCoordinate(Double.NaN,Double.NaN,
15                         Double.NaN,Double.NaN);
16         public static final double COMPARISON_DELTA = 0.000001;
17         private double x,y,z;
18         private double weight;
19         
20         
21         /* Count and report the number of times a Coordinate is constructed: */
22 //      private static int count=0;
23 //      {
24 //              count++;
25 //              if ((count % 1000) == 0) {
26 //                      System.out.println("Coordinate instantiated "+count+" times");
27 //              }
28 //      }
29         
30
31         public MutableCoordinate() {
32                 x=0;
33                 y=0;
34                 z=0;
35                 weight=0;
36         }
37         
38         public MutableCoordinate(double x) {
39                 this.x = x;
40                 this.y = 0;
41                 this.z = 0;
42                 weight = 0;
43         }
44         
45         public MutableCoordinate(double x, double y) {
46                 this.x = x;
47                 this.y = y;
48                 this.z = 0;
49                 weight = 0;
50         }
51         
52         public MutableCoordinate(double x, double y, double z) {
53                 this.x = x;
54                 this.y = y;
55                 this.z = z;
56                 weight = 0;
57         }
58         public MutableCoordinate(double x, double y, double z, double w) {
59                 this.x = x;
60                 this.y = y;
61                 this.z = z;
62                 this.weight=w;
63         }
64
65         
66         public boolean isWeighted() {
67                 return (weight != 0);
68         }
69         
70         public boolean isNaN() {
71                 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
72         }
73         
74         public MutableCoordinate setX(double x) {
75                 return new MutableCoordinate(x,this.y,this.z,this.weight);
76         }
77         
78         public MutableCoordinate setY(double y) {
79                 return new MutableCoordinate(this.x,y,this.z,this.weight);
80         }
81         
82         public MutableCoordinate setZ(double z) {
83                 return new MutableCoordinate(this.x,this.y,z,this.weight);
84         }
85         
86         public MutableCoordinate setWeight(double weight) {
87                 return new MutableCoordinate(this.x, this.y, this.z, weight);
88         }
89         
90         public MutableCoordinate setXYZ(MutableCoordinate c) {
91                 return new MutableCoordinate(c.x, c.y, c.z, this.weight);
92         }
93         
94         public double getX() {
95                 return x;
96         }
97         public double getY() {
98                 return y;
99         }
100         public double getZ() {
101                 return z;
102         }
103
104         
105         /**
106          * Add the coordinate and weight of two coordinates.
107          * 
108          * @param other  the other <code>Coordinate</code>
109          * @return               the sum of the coordinates
110          */
111         public MutableCoordinate add(MutableCoordinate other) {
112                 this.x += other.x;
113                 this.y += other.y;
114                 this.z += other.z;
115                 this.weight += other.weight;
116                 return this;
117         }
118         
119         public MutableCoordinate add(double x, double y, double z) {
120                 this.x += x;
121                 this.y += y;
122                 this.z += z;
123                 return this;
124         }
125
126         public MutableCoordinate add(double x, double y, double z, double weight) {
127                 return new MutableCoordinate(this.x+x, this.y+y, this.z+z, this.weight+weight);
128         }
129
130         /**
131          * Subtract a Coordinate from this Coordinate.  The weight of the resulting Coordinate
132          * is the same as of this Coordinate, the weight of the argument is ignored.
133          * 
134          * @param other  Coordinate to subtract from this.
135          * @return  The result
136          */
137         public MutableCoordinate sub(MutableCoordinate other) {
138                 return new MutableCoordinate(this.x-other.x, this.y-other.y, this.z-other.z, this.weight);
139         }
140
141         /**
142          * Subtract the specified values from this Coordinate.  The weight of the result
143          * is the same as the weight of this Coordinate.
144          * 
145          * @param x     x value to subtract
146          * @param y             y value to subtract
147          * @param z             z value to subtract
148          * @return              the result.
149          */
150         public MutableCoordinate sub(double x, double y, double z) {
151                 return new MutableCoordinate(this.x - x, this.y - y, this.z - z, this.weight);
152         }
153         
154         
155         /**
156          * Multiply the <code>Coordinate</code> with a scalar.  All coordinates and the
157          * weight are multiplied by the given scalar.
158
159          * @param m  Factor to multiply by.
160          * @return   The product. 
161          */
162         public MutableCoordinate multiply(double m) {
163                 return new MutableCoordinate(this.x*m, this.y*m, this.z*m, this.weight*m);
164         }
165
166         /**
167          * Dot product of two Coordinates, taken as vectors.  Equal to
168          * x1*x2+y1*y2+z1*z2
169          * @param other  Coordinate to take product with.
170          * @return   The dot product.
171          */
172         public double dot(MutableCoordinate other) {
173                 return this.x*other.x + this.y*other.y + this.z*other.z;
174         }
175         /**
176          * Dot product of two Coordinates.
177          */
178         public static double dot(MutableCoordinate v1, MutableCoordinate v2) {
179                 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
180         }
181
182         /**
183          * Distance from the origin to the Coordinate.
184          */
185         public double length() {
186                 return Math.sqrt(x*x+y*y+z*z);
187         }
188         
189         /**
190          * Square of the distance from the origin to the Coordinate.
191          */
192         public double length2() {
193                 return x*x+y*y+z*z;
194         }
195         
196         /**
197          * Returns a new coordinate which has the same direction from the origin as this
198          * coordinate but is at a distance of one.  If this coordinate is the origin,
199          * this method throws an <code>IllegalStateException</code>.  The weight of the
200          * coordinate is unchanged.
201          * 
202          * @return   the coordinate normalized to distance one of the origin.
203          * @throws   IllegalStateException  if this coordinate is the origin.
204          */
205         public MutableCoordinate normalize() {
206                 double l = length();
207                 if (l < 0.0000001) {
208                         throw new IllegalStateException("Cannot normalize zero coordinate");
209                 }
210                 return new MutableCoordinate(x/l, y/l, z/l, weight);
211         }
212         
213         
214         
215         
216         /**
217          * Weighted average of two coordinates.  If either of the weights are positive,
218          * the result is the weighted average of the coordinates and the weight is the sum
219          * of the original weights.  If the sum of the weights is zero (and especially if
220          * both of the weights are zero), the result is the unweighted average of the 
221          * coordinates with weight zero.
222          * <p>
223          * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
224          * returned.
225          */
226         public MutableCoordinate average(MutableCoordinate other) {
227                 double x,y,z,w;
228                 
229                 if (other == null)
230                         return this;
231                 
232                 w = this.weight + other.weight;
233                 if (MathUtil.equals(w, 0)) {
234                         x = (this.x+other.x)/2;
235                         y = (this.y+other.y)/2;
236                         z = (this.z+other.z)/2;
237                         w = 0;
238                 } else {
239                         x = (this.x*this.weight + other.x*other.weight)/w;
240                         y = (this.y*this.weight + other.y*other.weight)/w;
241                         z = (this.z*this.weight + other.z*other.weight)/w;
242                 }
243                 return new MutableCoordinate(x,y,z,w);
244         }
245         
246         
247         /**
248          * Tests whether the coordinates (not weight!) are the same.
249          * 
250          * Compares only the (x,y,z) coordinates, NOT the weight.  Coordinate comparison is
251          * done to the precision of COMPARISON_DELTA.
252          * 
253          * @param other  Coordinate to compare to.
254          * @return  true if the coordinates are equal
255          */
256         @Override
257         public boolean equals(Object other) {
258                 if (!(other instanceof MutableCoordinate))
259                         return false;
260                 
261                 final MutableCoordinate c = (MutableCoordinate)other;
262                 return (MathUtil.equals(this.x, c.x) &&
263                                 MathUtil.equals(this.y, c.y) &&
264                                 MathUtil.equals(this.z, c.z));
265         }
266         
267         /**
268          * Hash code method compatible with {@link #equals(Object)}.
269          */
270         @Override
271         public int hashCode() {
272                 return (int)((x+y+z)*100000);
273         }
274         
275         
276         @Override
277         public String toString() {
278                 if (isWeighted())
279                         return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
280                 else
281                         return String.format("(%.3f,%.3f,%.3f)", x,y,z);
282         }
283         
284         
285         
286         public static void main(String[] arg) {
287                 double a=1.2;
288                 double x;
289                 MutableCoordinate c;
290                 long t1, t2;
291                 
292                 x = 0;
293                 t1 = System.nanoTime();
294                 for (int i=0; i < 100000000; i++) {
295                         x = x + a;
296                 }
297                 t2 = System.nanoTime();
298                 System.out.println("Value: "+x);
299                 System.out.println("Plain addition: "+ ((t2-t1+500000)/1000000) + " ms");
300                 
301                 c = MutableCoordinate.NUL;
302                 t1 = System.nanoTime();
303                 for (int i=0; i < 100000000; i++) {
304                         c = c.add(a,0,0);
305                 }
306                 t2 = System.nanoTime();
307                 System.out.println("Value: "+c.x);
308                 System.out.println("Coordinate addition: "+ ((t2-t1+500000)/1000000) + " ms");
309                 
310         }
311         
312 }