/* * Copyright (c) 2016 Fraunhofer IGD * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Fraunhofer IGD <http://www.igd.fraunhofer.de/> */ package de.fhg.igd.geom.util; import com.google.common.base.Preconditions; /** * Some mathematical helper methods * * @author Simon Thum * @author Michel Kraemer */ public class MathHelper { /** * Enforces a value range * * @param in the input value * @param lower the lower limit * @param upper the upper limit * @return the saturated value */ public static int saturate(int in, int lower, int upper) { if (in >= lower && in <= upper) { return in; } if (in < lower) { return lower; } return upper; } /** * Enforces a value range * * @param in the input value * @param lower the lower limit * @param upper the upper limit * @return the saturated value */ public static double saturate(double in, double lower, double upper) { if (in >= lower && in <= upper) { return in; } if (in < lower) { return lower; } return upper; } /** * Enforces a value range * * @param in the input value * @param lower the lower limit * @param upper the upper limit * @return the saturated value */ public static float saturate(float in, float lower, float upper) { if (in >= lower && in <= upper) { return in; } if (in < lower) { return lower; } return upper; } /** * Translate a value based on input and output ranges. Does not saturate. * * @param in the value to translate * @param in_min minimum of the input range * @param in_range size of the input range * @param out_min minimum of the output range * @param out_range size of the output range * @return the translated value */ public static double translate(double in, double in_min, double in_range, double out_min, double out_range) { return (((in - in_min) / in_range) * out_range) + out_min; } /** * scales a double range using integer indexed segments. * * @param start the range start * @param end the range end * @param unit the unit (usually end-start / max ) * @param index the index to select * @param max the maximum index * @return a double */ public static double intScale(double start, double end, double unit, int index, int max) { if (index == 0) return start; else if (index == max) return end; return start + unit * index; } /** * SQL-like coalescing using isReal() * * @param p array of at least unit length * @return the first real value from the given array */ public static float coalesce(float... p) { for (float v : p) { if (isReal(v)) { return v; } } return p[p.length - 1]; } /** * Checks if a floating point number is real (not infinite and not NaN) * * @param x the number to check * @return true if x is real, false otherwise */ public static boolean isReal(float x) { return (!Float.isInfinite(x) && !Float.isNaN(x)); } /** * Checks if a floating point number is real (not infinite and not NaN) * * @param x the number to check * @return true if x is real, false otherwise */ public static boolean isReal(double x) { return (!Double.isInfinite(x) && !Double.isNaN(x)); } /** * The so-called 'euclidean' modulo, a modulo which won't yield negative * results * * @param x the number to divide * @param mod the divisor * @return the euclidean modulo */ public static int modulo(int x, int mod) { if (x >= 0) { return x % mod; } int n = 1 + (-x / mod); x += n * mod; return x % mod; } /** * Calculates the dot product between two vectors * * @param p1x the x ordinate of the first vector * @param p1y the y ordinate of the first vector * @param p2x the x ordinate of the second vector * @param p2y the y ordinate of the second vector * @return the dot product */ public static double dot2D(double p1x, double p1y, double p2x, double p2y) { return p1x * p2x + p1y * p2y; } /** * given two angels in radians, returns a difference in radians closest to * zero such that a + angleDiff(a, b) represents b. * * @param a angle a * @param b angle b * @return the angular difference in radians */ public static double angleDiff(double a, double b) { return angleDiff(a, b, Math.PI * 2); } /** * given two angels in radians, returns a difference in radians closest to * zero such that a + angleDiff(a, b) represents b. * * @param a angle a * @param b angle b * @param ring the ring in which the difference is meaningful (2PI is full * circle) * @return the angular difference in radians */ public static double angleDiff(double a, double b, double ring) { double c = b - a; c %= ring; if (c >= -ring / 2 && c <= ring / 2) return c; c += (-ring) * Math.signum(c); return c; } /** * given two angels in radians, returns a difference in radians closest to * zero and >= zero. * * @param a angle a * @param b angle b * @return the angular difference in radians */ public static double angleDiffAbs(double a, double b) { return Math.abs(angleDiff(a, b)); } /** * given two angels in radians, returns a difference in radians closest to * zero and >= zero, depending on the scale. For example, while two angles * pi/2 and -pi/2 may differ on a full circle (scale 1), they are considered * equal at scale 2. At scale 3, also orthogonal angles would match, and so * on. * * @param a angle a * @param b angle b * @param scale the scale to base the difference on * @return the angular difference in radians */ public static double angleDiffAbs(double a, double b, int scale) { return Math.abs(angleDiff(a, b, Math.scalb(Math.PI, 2 - scale))); } /** * Checks if a given integer is a power of two * * @param i the integer * @return true if i is a power of two */ public static boolean isPowerOfTwo(int i) { return ((i & (i - 1)) == 0); } /** * Finds the next power of two for a given integer (see * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2) * * @param i the integer (must be positive) * @return the next power of two */ public static int nextPowerOfTwo(int i) { Preconditions.checkArgument(i >= 0, "i must be positive"); i--; i |= i >> 1; i |= i >> 2; i |= i >> 4; i |= i >> 8; i |= i >> 16; i++; return i; } /** * Finds the next power of two lower than the given number * * @param i the number (must larger than 0) * @return the next power of two lower than i */ public static int previousPowerOfTwo(int i) { Preconditions.checkArgument(i > 0, "i must be larger than 0"); return nextPowerOfTwo(i) >> 1; } /** * Calls {@link #nextPowerOfTwo(int)} and {@link #previousPowerOfTwo(int)} * and finds the nearest value to the given number. Prefers the higher * number if the distance is equal. * * @param i the number (must be positive) * @return the nearest power of two */ public static int nearestPowerOfTwo(int i) { Preconditions.checkArgument(i >= 0, "i must be positive"); if (i == 0) { return 0; } int n = nextPowerOfTwo(i); int p = n >> 1; if (Math.abs(i - n) <= Math.abs(i - p)) { return n; } return p; } /** * Compares two double for equality within e * * @param a the first double * @param b the second double * @param e the maximum allowed difference * @return true if a - b < e */ public static boolean robustEqual(double a, double b, double e) { return Math.abs(a - b) < e; } }