1 package net.sf.openrocket.util;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
9 public class MathUtil {
10 public static final double EPSILON = 0.00000001; // 10mm^3 in m^3
13 * The square of x (x^2). On Sun's JRE using this method is as fast as typing x*x.
17 public static double pow2(double x) {
22 * The cube of x (x^3).
26 public static double pow3(double x) {
30 public static double pow4(double x) {
31 return (x * x) * (x * x);
35 * Clamps the value x to the range min - max.
36 * @param x Original value.
37 * @param min Minimum value to return.
38 * @param max Maximum value to return.
39 * @return The clamped value.
41 public static double clamp(double x, double min, double max) {
49 public static float clamp(float x, float min, float max) {
57 public static int clamp(int x, int min, int max) {
67 * Maps a value from one value range to another.
69 * @param value the value to map.
70 * @param fromMin the minimum of the starting range.
71 * @param fromMax the maximum of the starting range.
72 * @param toMin the minimum of the destination range.
73 * @param toMax the maximum of the destination range.
74 * @return the mapped value.
75 * @throws IllegalArgumentException if fromMin == fromMax, but toMin != toMax.
77 public static double map(double value, double fromMin, double fromMax,
78 double toMin, double toMax) {
79 if (equals(toMin, toMax))
81 if (equals(fromMin, fromMax)) {
82 throw new IllegalArgumentException("from range is singular and to range is not: " +
83 "value=" + value + " fromMin=" + fromMin + " fromMax=" + fromMax +
84 "toMin=" + toMin + " toMax=" + toMax);
86 return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
91 * Maps a coordinate from one value range to another.
93 * @param value the value to map.
94 * @param fromMin the minimum of the starting range.
95 * @param fromMax the maximum of the starting range.
96 * @param toMin the minimum coordinate of the destination;
97 * @param toMax the maximum coordinate of the destination;
98 * @return the mapped value.
99 * @throws IllegalArgumentException if fromMin == fromMax, but toMin != toMax.
101 public static Coordinate map(double value, double fromMin, double fromMax,
102 Coordinate toMin, Coordinate toMax) {
103 if (toMin.equals(toMax))
105 if (equals(fromMin, fromMax)) {
106 throw new IllegalArgumentException("from range is singular and to range is not: " +
107 "value=" + value + " fromMin=" + fromMin + " fromMax=" + fromMax +
108 "toMin=" + toMin + " toMax=" + toMax);
110 double a = (value - fromMin) / (fromMax - fromMin);
111 return toMax.multiply(a).add(toMin.multiply(1 - a));
116 * Compute the minimum of two values. This is performed by direct comparison.
117 * However, if one of the values is NaN and the other is not, the non-NaN value is
120 public static double min(double x, double y) {
123 return (x < y) ? x : y;
127 * Compute the maximum of two values. This is performed by direct comparison.
128 * However, if one of the values is NaN and the other is not, the non-NaN value is
131 public static double max(double x, double y) {
134 return (x < y) ? y : x;
138 * Compute the minimum of three values. This is performed by direct comparison.
139 * However, if one of the values is NaN and the other is not, the non-NaN value is
142 public static double min(double x, double y, double z) {
143 if (x < y || Double.isNaN(y)) {
151 * Compute the maximum of three values. This is performed by direct comparison.
152 * However, if one of the values is NaN and the other is not, the non-NaN value is
155 public static double max(double x, double y, double z) {
156 if (x > y || Double.isNaN(y)) {
164 * Calculates the hypotenuse <code>sqrt(x^2+y^2)</code>. This method is SIGNIFICANTLY
165 * faster than <code>Math.hypot(x,y)</code>.
167 public static double hypot(double x, double y) {
168 return Math.sqrt(x * x + y * y);
172 * Reduce the angle x to the range 0 - 2*PI.
173 * @param x Original angle.
174 * @return The equivalent angle in the range 0 ... 2*PI.
176 public static double reduce360(double x) {
177 double d = Math.floor(x / (2 * Math.PI));
178 return x - d * 2 * Math.PI;
182 * Reduce the angle x to the range -PI - PI.
184 * Either -PI and PI might be returned, depending on the rounding function.
186 * @param x Original angle.
187 * @return The equivalent angle in the range -PI ... PI.
189 public static double reduce180(double x) {
190 double d = Math.rint(x / (2 * Math.PI));
191 return x - d * 2 * Math.PI;
195 public static boolean equals(double a, double b) {
196 double absb = Math.abs(b);
198 if (absb < EPSILON / 2) {
200 return Math.abs(a) < EPSILON / 2;
202 return Math.abs(a - b) < EPSILON * absb;
207 * Return the sign of the number. This corresponds to Math.signum, but ignores
208 * the special cases of zero and NaN. The value returned for those is arbitrary.
210 * This method is about 4 times faster than Math.signum().
212 * @param x the checked value.
213 * @return -1.0 if x<0; 1.0 if x>0; otherwise either -1.0 or 1.0.
215 public static double sign(double x) {
216 return (x < 0) ? -1.0 : 1.0;
219 /* Math.abs() is about 3x as fast as this:
221 public static double abs(double x) {
222 return (x<0) ? -x : x;
227 public static double average(Collection<? extends Number> values) {
228 if (values.isEmpty()) {
234 for (Number n : values) {
235 avg += n.doubleValue();
241 public static double stddev(Collection<? extends Number> values) {
242 if (values.size() < 2) {
246 double avg = average(values);
249 for (Number n : values) {
250 stddev += pow2(n.doubleValue() - avg);
253 stddev = Math.sqrt(stddev / (count - 1));
257 public static double median(Collection<? extends Number> values) {
258 if (values.isEmpty()) {
262 List<Number> sorted = new ArrayList<Number>(values);
263 Collections.sort(sorted, new Comparator<Number>() {
265 public int compare(Number o1, Number o2) {
266 return Double.compare(o1.doubleValue(), o2.doubleValue());
270 int n = sorted.size();
272 return (sorted.get(n / 2).doubleValue() + sorted.get(n / 2 - 1).doubleValue()) / 2;
274 return sorted.get(n / 2).doubleValue();