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