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