change to 0.9.6pre
[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          * Returns a new coordinate which has the same direction from the origin as this
209          * coordinate but is at a distance of one.  If this coordinate is the origin,
210          * this method throws an <code>IllegalStateException</code>.  The weight of the
211          * coordinate is unchanged.
212          * 
213          * @return   the coordinate normalized to distance one of the origin.
214          * @throws   IllegalStateException  if this coordinate is the origin.
215          */
216         public Coordinate normalize() {
217                 double l = length();
218                 if (l < 0.0000001) {
219                         throw new IllegalStateException("Cannot normalize zero coordinate");
220                 }
221                 return new Coordinate(x/l, y/l, z/l, weight);
222         }
223         
224         
225         
226         
227         /**
228          * Weighted average of two coordinates.  If either of the weights are positive,
229          * the result is the weighted average of the coordinates and the weight is the sum
230          * of the original weights.  If the sum of the weights is zero (and especially if
231          * both of the weights are zero), the result is the unweighted average of the 
232          * coordinates with weight zero.
233          * <p>
234          * If <code>other</code> is <code>null</code> then this <code>Coordinate</code> is
235          * returned.
236          */
237         public Coordinate average(Coordinate other) {
238                 double x,y,z,w;
239                 
240                 if (other == null)
241                         return this;
242                 
243                 w = this.weight + other.weight;
244                 if (Math.abs(w) < MathUtil.pow2(MathUtil.EPSILON)) {
245                         x = (this.x+other.x)/2;
246                         y = (this.y+other.y)/2;
247                         z = (this.z+other.z)/2;
248                         w = 0;
249                 } else {
250                         x = (this.x*this.weight + other.x*other.weight)/w;
251                         y = (this.y*this.weight + other.y*other.weight)/w;
252                         z = (this.z*this.weight + other.z*other.weight)/w;
253                 }
254                 return new Coordinate(x,y,z,w);
255         }
256         
257         
258         /**
259          * Tests whether the coordinates (not weight!) are the same.
260          * 
261          * Compares only the (x,y,z) coordinates, NOT the weight.  Coordinate comparison is
262          * done to the precision of COMPARISON_DELTA.
263          * 
264          * @param other  Coordinate to compare to.
265          * @return  true if the coordinates are equal
266          */
267         @Override
268         public boolean equals(Object other) {
269                 if (!(other instanceof Coordinate))
270                         return false;
271                 
272                 final Coordinate c = (Coordinate)other;
273                 return (MathUtil.equals(this.x, c.x) &&
274                                 MathUtil.equals(this.y, c.y) &&
275                                 MathUtil.equals(this.z, c.z));
276         }
277         
278         /**
279          * Hash code method compatible with {@link #equals(Object)}.
280          */
281         @Override
282         public int hashCode() {
283                 return (int)((x+y+z)*100000);
284         }
285         
286         
287         @Override
288         public String toString() {
289                 if (isWeighted())
290                         return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x,y,z,weight);
291                 else
292                         return String.format("(%.3f,%.3f,%.3f)", x,y,z);
293         }
294         
295         
296 }