Package hype.core.util

Source Code of hype.core.util.HMath

/*
* HYPE_Processing
* http://www.hypeframework.org/ & https://github.com/hype/HYPE_Processing
*
* Copyright (c) 2013 Joshua Davis & James Cruz
*
* Distributed under the BSD License. See LICENSE.txt for details.
*
* All rights reserved.
*/

package hype.core.util;

import hype.core.drawable.HDrawable;
import processing.core.PConstants;
import processing.core.PVector;

/**
* A static class that provides some math methods.
*
* These methods are primarily used internally by the HYPE classes, but these
* can also be used externally. (e.g. `randomInt()` can be very handy in some
* sketches.)
*
* Most of these methods are here because any of the following:
* - It's only available as a static PApplet method. (We are trying to avoid it
*   due to Processing.js�not having a static PApplet object, forcing us to call
*   the math methods in a non-static context.)
* - It's not available in _both_ `java.lang.Math` and the standard Javascript
*   `Math` object.
* - It's a modification of an existing method.
* - It's not available at all in any of the above classes.
*
* @author james
*/
public class HMath implements HConstants {
  private static boolean _usingTempSeed;
  private static int _resetSeedValue;
 
  // GEOMETRY //
 
  public static float dist(float x1, float y1, float x2, float y2) {
    float w = x2 - x1;
    float h = y2 - y1;
    return (float) Math.sqrt(w*w + h*h);
  }
 
  public static float[] rotatePointArr(float x, float y, float rad) {
    float[] pt = new float[2];
   
    float c = (float) Math.cos(rad);
    float s = (float) Math.sin(rad);
   
    pt[0] = x*c - y*s;
    pt[1] = x*s + y*c;
   
    return pt;
  }
 
  public static PVector rotatePoint(float x, float y, float rad) {
    float[] f = rotatePointArr(x,y,rad);
    return new PVector(f[0], f[1]);
  }
 
  public static float yAxisAngle(float x1, float y1, float x2, float y2) {
    return (float) Math.atan2(x2-x1, y2-y1);
  }
 
  public static float xAxisAngle(float x1, float y1, float x2, float y2) {
    return (float) Math.atan2(y2-y1, x2-x1);
  }
 
  public static float[] absLocArr(HDrawable ref, float relX, float relY) {
    float[] f = {relX, relY, 0};
    while(ref != null) {
      float rot = ref.rotationRad();
      float[] g = rotatePointArr(f[0], f[1], rot);
     
      f[0] = g[0] + ref.x();
      f[1] = g[1] + ref.y();
      f[2] += rot;
     
      ref = ref.parent();
    }
    return f;
  }
 
  public static PVector absLoc(HDrawable ref, float relX, float relY) {
    float[] f = absLocArr(ref,relX,relY);
    return new PVector(f[0], f[1]);
  }
 
  public static PVector absLoc(HDrawable d) {
    return absLoc(d,0,0);
  }
 
  public static float[] relLocArr(HDrawable ref, float absX, float absY) {
    float[] f = absLocArr(ref,0,0);
    return rotatePointArr(absX-f[0], absY-f[1], -f[2]);
  }
 
  public static PVector relLoc(HDrawable ref, float absX, float absY) {
    float[] f = relLocArr(ref,absX,absY);
    return new PVector(f[0], f[1]);
  }
 
  public static int quadrant(float cx, float cy, float x, float y) {
    return (y>=cy)? (x>=cx? 1 : 2) : (x>=cx? 4 : 3);
  }
 
  public static int quadrant(float dcx, float dcy) {
    return (dcy>=0)? (dcx>=0? 1 : 2) : (dcx>=0? 4 : 3);
  }
 
  public static float ellipseRadius(float a, float b, float deg) {
    return ellipseRadiusRad(a,b, deg * D2R);
  }
 
  public static float ellipseRadiusRad(float a, float b, float rad) {
    float cosb = b * (float)Math.cos(rad);
    float sina = a * (float)Math.sin(rad);
    return a*b / (float)Math.sqrt(cosb*cosb + sina*sina);
  }
 
  public static PVector ellipsePoint(
      float cx, float cy, float a, float b, float deg
  ) {
    return ellipsePointRad(cx, cy, a, b, deg*D2R);
  }
 
  public static PVector ellipsePointRad(
    float cx, float cy, float a, float b, float rad
  ) {
    float[] f = ellipsePointRadArr(cx,cy, a,b, rad);
    return new PVector(f[0], f[1]);
  }
 
  public static float[] ellipsePointRadArr(
    float cx, float cy, float a, float b, float rad
  ) {
    float[] f = new float[3];
    f[2] = ellipseRadiusRad(a, b, rad);
    f[0] = f[2] * (float)Math.cos(rad) + cx;
    f[1] = f[2] * (float)Math.sin(rad) + cy;
    return f;
  }
 
  // TODO rename to normalizeDeg() and normalizeRad()
  // TODO have the deg method versions of these directly compute the angles
  //      instead of delegating to the rad versions
  public static float normalizeAngle(float deg) {
    return normalizeAngleRad(deg * D2R) * R2D;
  }
 
  public static float normalizeAngleRad(float rad) {
    rad %= PConstants.TWO_PI;
    if(rad < -PConstants.PI) rad += PConstants.TWO_PI;
    else if(rad > PConstants.PI) rad -= PConstants.TWO_PI;
    return rad;
  }
 
  public static float normalizeAngle2(float deg) {
    return normalizeAngleRad2(deg * D2R) * R2D;
  }
 
  public static float normalizeAngleRad2(float rad) {
    float norm = rad % PConstants.TWO_PI;
    if(norm < 0) norm += PConstants.TWO_PI;
    return norm;
  }
 
  public static float squishAngle(float w, float h, float deg) {
    return squishAngle(w, h, deg * D2R) * R2D;
  }
 
  public static float squishAngleRad(float w, float h, float rad) {
    float dx = (float)Math.cos(rad) * w/h;
    float dy = (float)Math.sin(rad);
    return (float) Math.atan2(dy,dx);
  }
 
  public static float lineSide(
    float x1, float y1, float x2, float y2, float ptx, float pty
  ) {
    return (x2-x1)*(pty-y1) - (y2-y1)*(ptx-x1);
  }
 
  public static boolean collinear(
    float x1, float y1, float x2, float y2, float ptx, float pty
  ) {
    return (lineSide(x1,y1, x2,y2, ptx,pty) == 0);
  }
 
  public static boolean leftOfLine(
    float x1, float y1, float x2, float y2, float ptx, float pty
  ) {
    return (lineSide(x1,y1, x2,y2, ptx,pty) < 0);
  }
 
  public static boolean rightOfLine(
    float x1, float y1, float x2, float y2, float ptx, float pty
  ) {
    return (lineSide(x1,y1, x2,y2, ptx,pty) > 0);
  }
 
  /**
   * Solves for the real roots of a cubic equation with the given coefficients.
   *
   * Said equation is in the standard polynomial form:
   *
   *     ax^3 + bx^2 + cx + d = 0
   *
   * where `a`, `b`, `c` & `d` are the coefficients of said equation.
   *
   * Since the maximum amount of real roots that a cubic equation can have is
   * three, the `roots` array is expected to have a size of at least 3.
   *
   * If this equation happens to be a straight line lying on the x-axis, then
   * this method will return `-1`, meaning that there are infinite number of
   * roots for this equation.
   *
   * This method may delegate itself to `solveQuadratic()` if `a` equals 0
   * (in other words, the equation is quadratic.)
   *
   * The code for this method is heavily based from paper.js (MIT License)
   * which in turn is based from the Uintah Library (also MIT License).
   *
   * @see solveQuadratic(float,float,float,float[])
   * @param a    The coefficient for the first term (ax^3)
   * @param b    The coefficient for the second term (bx^2)
   * @param c    The coefficient for the third term (cx)
   * @param d    The coefficient for the fourth term (d)
   * @param roots    The array that will contain the roots of the equation
   * @return The number of roots of the given equation
   */
  public static int solveCubic(
    float a, float b, float c, float d, float[] roots
  ) {
    if(Math.abs(a) < EPSILON) return solveQuadratic(b,c,d,roots);
    b /= a;
    c /= a;
    d /= a;
    float bb = b*b;
    float p = (bb - 3*c) / 9f;
    float ppp = p*p*p;
    float q = (2*bb*b - 9*b*c + 27*d) / 54;
    float D = q*q - ppp;
    b /= 3f;
   
    // D == 0; one or two real roots
    if(Math.abs(D) < EPSILON) {
      if(Math.abs(q) < EPSILON) {
        roots[0] = -b;
        return 1;
      }
      float sqrtp = (float)Math.sqrt(p);
      float signq = (q>0)? 1 : -1;
      roots[0] = -signq*2*sqrtp - b;
      roots[1] = signq*sqrtp - b;
      return 2;
    }
   
    // D < 0; three real roots
    if(D < 0) {
      float sqrtp = (float)Math.sqrt(p);
      float phi = (float)Math.acos(q / (sqrtp*sqrtp*sqrtp)) / 3;
      float t = -2*sqrtp;
      float o = PConstants.TWO_PI/3f;
      roots[0] = t*(float)Math.cos(phi) - b;
      roots[1] = t*(float)Math.cos(phi + o) - b;
      roots[2] = t*(float)Math.cos(phi - o) - b;
      return 3;
    }
   
    // D > 0; one real root
    float A = (q>0?-1:1) *
      (float)Math.pow(Math.abs(q) + Math.sqrt(D), 1.0/3.0);
    roots[0] = A + p/A - b;
    return 1;
  }
 
  /**
   * Solves for the real roots of a quadratic equation with the given
   * coefficients.
   *
   * Said equation is in the standard polynomial form:
   *
   *     ax^2 + bx + c = 0
   *
   * where `a`, `b` & `c` are the coefficients of said equation.
   *
   * Since the maximum amount of roots that a quadratic equation can have is
   * two, the `roots` array is expected to have a size of at least 2.
   *
   * If this equation happens to be a straight line lying on the x-axis, then
   * this method will return `-1`, meaning that there are infinite number of
   * roots for this equation.
   *
   * The code for this method is heavily based from paper.js (MIT License)
   * which in turn is based from the Uintah Library (also MIT License).
   *
   * @see solveCubic(float,float,float,float,float[])
   * @param a    The coefficient for the first term (`ax^2`)
   * @param b    The coefficient for the second term (`bx`)
   * @param c    The coefficient for the third term (`c`)
   * @param roots    The array that will contain the roots of the equation
   * @return The number of roots of the given equation
   */
  public static int solveQuadratic(float a, float b, float c, float[] roots) {
    if(Math.abs(a) < EPSILON) {
      if(Math.abs(b) >= EPSILON) {
        roots[0] = -c/b;
        return 1;
      }
      // -1 means infinite solutions; i.e. the equation
      // is a horizontal line lying on the x-axis
      return (Math.abs(c)<EPSILON)? -1 : 0;
    }
    float q = b*b - 4*a*c;
    if(q < 0) return 0;
   
    q = (float)Math.sqrt(q);
    a *= 2;
    int numRoots = 0;
   
    roots[numRoots++] = (-b-q) / a;
    if(q > 0) roots[numRoots++] = (-b+q) / a;
    return numRoots;
  }
 
  /**
   * Solves for the valid parameters of a given cubic bezier equation.
   *
   * This method calls `solveCubic()` to solve for the said equation.
   *
   * Since cubic bezier curves can have at most three parameters that could
   * give the same value, the `params` array is expected to have a size of at
   * least 3. Note that these parameters can be less than `0` or more than
   * `1`, so you will need to check them before using the contents of the
   * `params` array.
   *
   * The `val` argument is the value to be tested, where:
   *
   *     val = bezier(p0,p1,p2,p3,param)
   *
   * If the above equation is in such a way that it could not be true, then
   * this method will return `0` valid parameters.
   *
   * @see solveCubic(float,float,float,float,float[]), bezierParam(float,float,float,float,float[])
   * @param p0    The first anchor point
   * @param p1    The first control point
   * @param p2    The second control point
   * @param p3    The second anchor point
   * @param val   The value to be tested with the curve
   * @param params    The array that will contain the parameters of the bezier equation
   * @return The number of valid parameters of the given bezier equation
   */
  public static int bezierParam(
    float p0, float p1, float p2, float p3,
    float val, float[] params
  ) {
    // If `val` is outside the control & anchor points, return 0
    float max = p0;
    if(max < p1) max = p1;
    if(max < p2) max = p2;
    if(max < p3) max = p3;
   
    float min = p0;
    if(min > p1) min = p1;
    if(min > p2) min = p2;
    if(min > p3) min = p3;
   
    if(val<min || val>max) return 0;
   
   
    float a = 3*(p1-p2) - p0 + p3;
    float b = 3*(p0 - 2*p1 + p2);
    float c = 3*(p1-p0);
    float d = p0 - val;
    return solveCubic(a,b,c,d,params);
  }

  public static int bezierParam(
    float p0, float p1, float p2,
    float val, float[] params
  ) {
    // If `val` is outside the control & anchor points, return 0
    float max = p0;
    if(max < p1) max = p1;
    if(max < p2) max = p2;
   
    float min = p0;
    if(min > p1) min = p1;
    if(min > p2) min = p2;
   
    if(val<min || val>max) return 0;
   
   
    float a = p2 - 2*p1 + p0;
    float b = 2 * (p1-p0);
    float c = p0 - val;
    return solveQuadratic(a,b,c,params);
  }

  public static float bezierPoint(
    float p0, float p1, float p2, float p3, float t
  ) {
    float tt = t*t;
    float a = 3*(p1-p2) - p0 + p3;
    float b = 3*(p0 - 2*p1 + p2);
    float c = 3*(p1-p0);
    return a*tt*t + b*tt + c*t + p0;
  }
 
  public static float bezierPoint(float p0, float p1, float p2, float t) {
    float a = p2 - 2*p1 + p0;
    float b = 2 * (p1-p0);
    return a*t*t + b*t + p0;
  }
 
  public static float bezierTangent(
    float p0, float p1, float p2, float p3, float t
  ) {
    float a = 3 * (3*(p1-p2) - p0 + p3);
    float b = 6 * (p0 - 2*p1 + p2);
    float c = 3 * (p1-p0);
    return a*t*t + b*t + c;
  }
 
  public static float bezierTangent(float p0, float p1, float p2, float t) {
    float a = 2 * (p2 - 2*p1 + p0);
    float b = 2 * (p1-p0);
    return a*t + b;
  }
 
 
  // RNG //
 
  public static int randomInt(float high) {
    return (int) Math.floor( H.app().random(high) );
  }
 
  public static int randomInt(float low, float high) {
    return (int) Math.floor( H.app().random(low,high) );
  }
 
  public static int randomInt32() {
    return randomInt(-2147483648,2147483647);
  }
 
  public static void tempSeed(long seed) {
    if(!_usingTempSeed) {
      _resetSeedValue = randomInt32();
      _usingTempSeed = true;
    }
    H.app().randomSeed(seed);
  }
 
  public static void removeTempSeed() {
    H.app().randomSeed(_resetSeedValue);
  }
 
 
  // WAVES //
 
  public static float sineWave(float stepDegrees) {
    return (float) Math.sin(stepDegrees * H.D2R);
  }
 
  public static float triangleWave(float stepDegrees) {
    float outVal = (stepDegrees % 180) / 90;
    if(outVal > 1)
      outVal = 2-outVal;
    if(stepDegrees % 360 > 180)
      outVal = -outVal;
    return outVal;
  }
 
  public static float sawWave(float stepDegrees) {
    float outVal = (stepDegrees % 180) / 180;
    if(stepDegrees % 360 >= 180)
      outVal -= 1;
    return outVal;
  }
 
  public static float squareWave(float stepDegrees) {
    return (stepDegrees % 360 > 180)? -1 : 1;
  }
 
 
  // BITWISE OPERATIONS //
 
  public static boolean hasBits(byte target, byte mask) {
    return (target&mask) == mask;
  }
 
  public static boolean hasBits(int target, int mask) {
    return (target&mask) == mask;
  }
 
  public static byte setBits(byte target, byte mask, boolean val) {
    return (byte) (val? target|mask : target&(~mask));
  }
 
  public static int setBits(int target, int mask, boolean val) {
    return (val)? target|mask : target&(~mask);
  }
 
 
  // COMPARATORS W/ TOLERANCE //
 
  public static boolean lessThan(float a, float b, float tolerance) {
    return a < b + tolerance;
  }
 
  public static boolean lessThan(float a, float b) {
    return a < b + TOLERANCE;
  }
 
  public static boolean greaterThan(float a, float b, float tolerance) {
    return b < a + tolerance;
  }
 
  public static boolean greaterThan(float a, float b) {
    return b < a + TOLERANCE;
  }
 
  public static boolean isEqual(float a, float b, float tolerance) {
    return Math.abs(a-b) < tolerance;
  }
 
  public static boolean isEqual(float a, float b) {
    return Math.abs(a-b) < TOLERANCE;
  }
 
  public static boolean isZero(float a, float tolerance) {
    return Math.abs(a) < tolerance;
  }
 
  public static boolean isZero(float a) {
    return Math.abs(a) < TOLERANCE;
  }
 
 
  // MISC //
 
  public static float map(float val,
    float start1, float stop1, float start2, float stop2
  ) {
    return start2 + (stop2-start2) * (val-start1)/(stop1-start1);
  }
 
  public static float round512(float val) {
    return Math.round(val*512)/512f;
  }
TOP

Related Classes of hype.core.util.HMath

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.