updates for 0.9.3
[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         
54         public static final Coordinate NUL = new Coordinate(0,0,0,0);
55         public static final Coordinate NaN = new Coordinate(Double.NaN,Double.NaN,
56                         Double.NaN,Double.NaN);
57
58         public final double x,y,z;
59         public final double weight;
60         
61         
62         private double length = -1;  /* Cached when calculated */
63         
64         
65         
66
67         public Coordinate() {
68                 this(0,0,0,0);
69         }
70         
71         public Coordinate(double x) {
72                 this(x,0,0,0);
73         }
74         
75         public Coordinate(double x, double y) {
76                 this(x,y,0,0);
77         }
78         
79         public Coordinate(double x, double y, double z) {
80                 this(x,y,z,0);
81         }
82         public Coordinate(double x, double y, double z, double w) {
83                 this.x = x;
84                 this.y = y;
85                 this.z = z;
86                 this.weight=w;
87                 
88         }
89
90         
91         public boolean isWeighted() {
92                 return (weight != 0);
93         }
94         
95         public boolean isNaN() {
96                 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Double.isNaN(weight);
97         }
98         
99         public Coordinate setX(double x) {
100                 return new Coordinate(x,this.y,this.z,this.weight);
101         }
102         
103         public Coordinate setY(double y) {
104                 return new Coordinate(this.x,y,this.z,this.weight);
105         }
106         
107         public Coordinate setZ(double z) {
108                 return new Coordinate(this.x,this.y,z,this.weight);
109         }
110         
111         public Coordinate setWeight(double weight) {
112                 return new Coordinate(this.x, this.y, this.z, weight);
113         }
114         
115         public Coordinate setXYZ(Coordinate c) {
116                 return new Coordinate(c.x, c.y, c.z, this.weight);
117         }
118
119         
120         /**
121          * Add the coordinate and weight of two coordinates.
122          * 
123          * @param other  the other <code>Coordinate</code>
124          * @return               the sum of the coordinates
125          */
126         public Coordinate add(Coordinate other) {
127                 return new Coordinate(this.x+other.x, this.y+other.y, this.z+other.z, 
128                                 this.weight+other.weight);
129         }
130         
131         public Coordinate add(double x, double y, double z) {
132                 return new Coordinate(this.x+x, this.y+y, this.z+z, this.weight);
133         }
134
135         public Coordinate add(double x, double y, double z, double weight) {
136                 return new Coordinate(this.x+x, this.y+y, this.z+z, this.weight+weight);
137         }
138
139         /**
140          * Subtract a Coordinate from this Coordinate.  The weight of the resulting Coordinate
141          * is the same as of this Coordinate, the weight of the argument is ignored.
142          * 
143          * @param other  Coordinate to subtract from this.
144          * @return  The result
145          */
146         public Coordinate sub(Coordinate other) {
147                 return new Coordinate(this.x-other.x, this.y-other.y, this.z-other.z, this.weight);
148         }
149
150         /**
151          * Subtract the specified values from this Coordinate.  The weight of the result
152          * is the same as the weight of this Coordinate.
153          * 
154          * @param x     x value to subtract
155          * @param y             y value to subtract
156          * @param z             z value to subtract
157          * @return              the result.
158          */
159         public Coordinate sub(double x, double y, double z) {
160                 return new Coordinate(this.x - x, this.y - y, this.z - z, this.weight);
161         }
162         
163         
164         /**
165          * Multiply the <code>Coordinate</code> with a scalar.  All coordinates and the
166          * weight are multiplied by the given scalar.
167
168          * @param m  Factor to multiply by.
169          * @return   The product. 
170          */
171         public Coordinate multiply(double m) {
172                 return new Coordinate(this.x*m, this.y*m, this.z*m, this.weight*m);
173         }
174
175         /**
176          * Dot product of two Coordinates, taken as vectors.  Equal to
177          * x1*x2+y1*y2+z1*z2
178          * @param other  Coordinate to take product with.
179          * @return   The dot product.
180          */
181         public double dot(Coordinate other) {
182                 return this.x*other.x + this.y*other.y + this.z*other.z;
183         }
184         /**
185          * Dot product of two Coordinates.
186          */
187         public static double dot(Coordinate v1, Coordinate v2) {
188                 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
189         }
190
191         /**
192          * Distance from the origin to the Coordinate.
193          */
194         public double length() {
195                 if (length < 0) {
196                         length = Math.sqrt(x*x+y*y+z*z); 
197                 }
198                 return length;
199         }
200         
201         /**
202          * Square of the distance from the origin to the Coordinate.
203          */
204         public double length2() {
205                 return x*x+y*y+z*z;
206         }
207         
208         /**
209          * Returns a new coordinate which has the same direction from the origin as this
210          * coordinate but is at a distance of one.  If this coordinate is the origin,
211          * this method throws an <code>IllegalStateException</code>.  The weight of the
212          * coordinate is unchanged.
213          * 
214          * @return   the coordinate normalized to distance one of the origin.
215          * @throws   IllegalStateException  if this coordinate is the origin.
216          */
217         public Coordinate normalize() {
218                 double l = length();
219                 if (l < 0.0000001) {
220                         throw new IllegalStateException("Cannot normalize zero coordinate");
221                 }
222                 return new Coordinate(x/l, y/l, z/l, weight);
223         }
224         
225         
226         
227         
228         /**
229          * Weighted average of two coordinates.  If either of the weights are positive,
230          * the result is the weighted average of the coordinates and the weight is the sum
231          * of the original weights.  If the sum of the weights is zero (and especially if
232          * both of the weights are zero), the result is the unweighted average of the 
233          * coordinates with weight zero.
234          * <p>
235          * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
236          * returned.
237          */
238         public Coordinate average(Coordinate other) {
239                 double x,y,z,w;
240                 
241                 if (other == null)
242                         return this;
243                 
244                 w = this.weight + other.weight;
245                 if (Math.abs(w) < MathUtil.pow2(MathUtil.EPSILON)) {
246                         x = (this.x+other.x)/2;
247                         y = (this.y+other.y)/2;
248                         z = (this.z+other.z)/2;
249                         w = 0;
250                 } else {
251                         x = (this.x*this.weight + other.x*other.weight)/w;
252                         y = (this.y*this.weight + other.y*other.weight)/w;
253                         z = (this.z*this.weight + other.z*other.weight)/w;
254                 }
255                 return new Coordinate(x,y,z,w);
256         }
257         
258         
259         /**
260          * Tests whether the coordinates (not weight!) are the same.
261          * 
262          * Compares only the (x,y,z) coordinates, NOT the weight.  Coordinate comparison is
263          * done to the precision of COMPARISON_DELTA.
264          * 
265          * @param other  Coordinate to compare to.
266          * @return  true if the coordinates are equal
267          */
268         @Override
269         public boolean equals(Object other) {
270                 if (!(other instanceof Coordinate))
271                         return false;
272                 
273                 final Coordinate c = (Coordinate)other;
274                 return (MathUtil.equals(this.x, c.x) &&
275                                 MathUtil.equals(this.y, c.y) &&
276                                 MathUtil.equals(this.z, c.z));
277         }
278         
279         /**
280          * Hash code method compatible with {@link #equals(Object)}.
281          */
282         @Override
283         public int hashCode() {
284                 return (int)((x+y+z)*100000);
285         }
286         
287         
288         @Override
289         public String toString() {
290                 if (isWeighted())
291                         return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
292                 else
293                         return String.format("(%.3f,%.3f,%.3f)", x,y,z);
294         }
295         
296         
297 }