Moved general purpose stuff into util, renamed utils->util

This commit is contained in:
ondra
2014-04-16 14:32:56 +02:00
parent e58156b7ee
commit 6843e746bc
208 changed files with 656 additions and 629 deletions
+934
View File
@@ -0,0 +1,934 @@
package mightypork.util.math;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import mightypork.util.math.constraints.vect.Vect;
import org.lwjgl.BufferUtils;
/**
* Math helper
*
* @author MightyPork
*/
public class Calc {
/** Square root of two */
public static final double SQ2 = 1.41421356237;
/**
* Get distance from 2D line to 2D point [X,Y]
*
* @param lineDirVec line directional vector
* @param linePoint point of line
* @param point point coordinate
* @return distance
*/
public static double linePointDist(Vect lineDirVec, Vect linePoint, Vect point)
{
// line point L[lx,ly]
final double lx = linePoint.x();
final double ly = linePoint.y();
// line equation ax+by+c=0
final double a = -lineDirVec.y();
final double b = lineDirVec.x();
final double c = -a * lx - b * ly;
// checked point P[x,y]
final double x = point.x();
final double y = point.y();
// distance
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
}
/**
* Get longest side of a right-angled triangle
*
* @param a side a (opposite)
* @param b side b (adjacent)
* @return longest side (hypotenuse)
*/
public static double pythC(double a, double b)
{
return Math.sqrt(square(a) + square(b));
}
/**
* Get adjacent side of a right-angled triangle
*
* @param a side a (opposite)
* @param c side c (hypotenuse)
* @return side b (adjacent)
*/
public static double pythB(double a, double c)
{
return Math.sqrt(square(c) - square(a));
}
/**
* Get opposite side of a right-angled triangle
*
* @param b side b (adjacent)
* @param c side c (hypotenuse)
* @return side a (opposite)
*/
public static double pythA(double b, double c)
{
return Math.sqrt(square(c) - square(b));
}
private static class Angles {
public static double delta(double alpha, double beta, double a360)
{
while (Math.abs(alpha - beta) > a360 / 2D) {
alpha = norm(alpha + a360 / 2D, a360);
beta = norm(beta + a360 / 2D, a360);
}
return beta - alpha;
}
public static double norm(double angle, double a360)
{
while (angle < 0)
angle += a360;
while (angle > a360)
angle -= a360;
return angle;
}
}
/**
* Calc subclass with buffer utils.
*
* @author MightyPork
*/
public static class Buffers {
/**
* Create java.nio.FloatBuffer of given floats, and flip it.
*
* @param obj floats or float array
* @return float buffer
*/
public static FloatBuffer mkFillBuff(float... obj)
{
return (FloatBuffer) BufferUtils.createFloatBuffer(obj.length).put(obj).flip();
}
/**
* Fill java.nio.FloatBuffer with floats or float array
*
* @param buff
* @param obj
*/
public static void fill(FloatBuffer buff, float... obj)
{
buff.put(obj);
buff.flip();
}
/**
* Create new java.nio.FloatBuffer of given length
*
* @param count elements
* @return the new java.nio.FloatBuffer
*/
public static FloatBuffer alloc(int count)
{
return BufferUtils.createFloatBuffer(count);
}
}
/**
* Angle calculations for degrees.
*
* @author MightyPork
*/
public static class Deg {
/** 180° in degrees */
public static final double a180 = 180;
/** 270° in degrees */
public static final double a270 = 270;
/** 360° in degrees */
public static final double a360 = 360;
/** 45° in degrees */
public static final double a45 = 45;
/** 90° in degrees */
public static final double a90 = 90;
/**
* Subtract two angles alpha - beta
*
* @param alpha first angle
* @param beta second angle
* @return (alpha - beta) in degrees
*/
public static double delta(double alpha, double beta)
{
return Angles.delta(alpha, beta, a360);
}
/**
* Difference of two angles (absolute value of delta)
*
* @param alpha first angle
* @param beta second angle
* @return difference in radians
*/
public static double diff(double alpha, double beta)
{
return Math.abs(Angles.delta(alpha, beta, a360));
}
/**
* Cosinus in degrees
*
* @param deg angle in degrees
* @return cosinus
*/
public static double cos(double deg)
{
return Math.cos(toRad(deg));
}
/**
* Sinus in degrees
*
* @param deg angle in degrees
* @return sinus
*/
public static double sin(double deg)
{
return Math.sin(toRad(deg));
}
/**
* Tangents in degrees
*
* @param deg angle in degrees
* @return tangents
*/
public static double tan(double deg)
{
return Math.tan(toRad(deg));
}
/**
* Angle normalized to 0-360 range
*
* @param angle angle to normalize
* @return normalized angle
*/
public static double norm(double angle)
{
return Angles.norm(angle, a360);
}
/**
* Convert to radians
*
* @param deg degrees
* @return radians
*/
public static double toRad(double deg)
{
return Math.toRadians(deg);
}
/**
* Round angle to 0,45,90,135...
*
* @param deg angle in deg. to round
* @param x rounding increment (45 - round to 0,45,90...)
* @return rounded
*/
public static int roundX(double deg, double x)
{
final double half = x / 2d;
deg += half;
deg = norm(deg);
final int times = (int) Math.floor(deg / x);
double a = times * x;
if (a == 360) a = 0;
return (int) Math.round(a);
}
/**
* Round angle to 0,45,90,135...
*
* @param deg angle in deg. to round
* @return rounded
*/
public static int round45(double deg)
{
return roundX(deg, 45);
}
/**
* Round angle to 0,90,180,270
*
* @param deg angle in deg. to round
* @return rounded
*/
public static int round90(double deg)
{
return roundX(deg, 90);
}
/**
* Round angle to 0,15,30,45,60,75,90...
*
* @param deg angle in deg to round
* @return rounded
*/
public static int round15(double deg)
{
return roundX(deg, 15);
}
}
/**
* Angle calculations for radians.
*
* @author MightyPork
*/
public static class Rad {
/** 180° in radians */
public static final double a180 = Math.PI;
/** 270° in radians */
public static final double a270 = Math.PI * 1.5D;
/** 360° in radians */
public static final double a360 = Math.PI * 2D;
/** 45° in radians */
public static final double a45 = Math.PI / 4D;
/** 90° in radians */
public static final double a90 = Math.PI / 2D;
/**
* Subtract two angles alpha - beta
*
* @param alpha first angle
* @param beta second angle
* @return (alpha - beta) in radians
*/
public static double delta(double alpha, double beta)
{
return Angles.delta(alpha, beta, a360);
}
/**
* Difference of two angles (absolute value of delta)
*
* @param alpha first angle
* @param beta second angle
* @return difference in radians
*/
public static double diff(double alpha, double beta)
{
return Math.abs(Angles.delta(alpha, beta, a360));
}
/**
* Cos
*
* @param rad angle in rads
* @return cos
*/
public static double cos(double rad)
{
return Math.cos(rad);
}
/**
* Sin
*
* @param rad angle in rads
* @return sin
*/
public static double sin(double rad)
{
return Math.sin(rad);
}
/**
* Tan
*
* @param rad angle in rads
* @return tan
*/
public static double tan(double rad)
{
return Math.tan(rad);
}
/**
* Angle normalized to 0-2*PI range
*
* @param angle angle to normalize
* @return normalized angle
*/
public static double norm(double angle)
{
return Angles.norm(angle, a360);
}
/**
* Convert to degrees
*
* @param rad radians
* @return degrees
*/
public static double toDeg(double rad)
{
return Math.toDegrees(rad);
}
}
private static Random rand = new Random();
/**
* Get volume of a sphere
*
* @param radius sphere radius
* @return volume in cubic units
*/
public static double sphereGetVolume(double radius)
{
return (4D / 3D) * Math.PI * cube(radius);
}
/**
* Get radius of a sphere
*
* @param volume sphere volume
* @return radius in units
*/
public static double sphereGetRadius(double volume)
{
return Math.cbrt((3D * volume) / (4 * Math.PI));
}
/**
* Get surface of a circle
*
* @param radius circle radius
* @return volume in square units
*/
public static double circleGetSurface(double radius)
{
return Math.PI * square(radius);
}
/**
* Get radius of a circle
*
* @param surface circle volume
* @return radius in units
*/
public static double circleGetRadius(double surface)
{
return Math.sqrt(surface / Math.PI);
}
/**
* Check if objects are equal (for equals function)
*
* @param a
* @param b
* @return are equal
*/
public static boolean areObjectsEqual(Object a, Object b)
{
return a == null ? b == null : a.equals(b);
}
/**
* Private clamping helper.
*
* @param number number to be clamped
* @param min min value
* @param max max value
* @return clamped double
*/
private static double clamp_double(Number number, Number min, Number max)
{
double n = number.doubleValue();
final double mind = min.doubleValue();
final double maxd = max.doubleValue();
if (n > maxd) n = maxd;
if (n < mind) n = mind;
if (Double.isNaN(n)) return mind;
return n;
}
/**
* Private clamping helper.
*
* @param number number to be clamped
* @param min min value
* @return clamped double
*/
private static double clamp_double(Number number, Number min)
{
double n = number.doubleValue();
final double mind = min.doubleValue();
if (n < mind) n = mind;
return n;
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* DOUBLE version
*
* @param number clamped number
* @param min minimal allowed value
* @param max maximal allowed value
* @return result
*/
public static double clampd(Number number, Number min, Number max)
{
return clamp_double(number, min, max);
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* FLOAT version
*
* @param number clamped number
* @param min minimal allowed value
* @param max maximal allowed value
* @return result
*/
public static float clampf(Number number, Number min, Number max)
{
return (float) clamp_double(number, min, max);
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* INTEGER version
*
* @param number clamped number
* @param min minimal allowed value
* @param max maximal allowed value
* @return result
*/
public static int clampi(Number number, Number min, Number max)
{
return (int) Math.round(clamp_double(number, min, max));
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* INTEGER version
*
* @param number clamped number
* @param range range
* @return result
*/
public static int clampi(Number number, Range range)
{
return (int) Math.round(clamp_double(number, range.getMin(), range.getMax()));
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* DOUBLE version
*
* @param number clamped number
* @param range range
* @return result
*/
public static double clampd(Number number, Range range)
{
return clamp_double(number, range.getMin(), range.getMax());
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* FLOAT version
*
* @param number clamped number
* @param range range
* @return result
*/
public static float clampf(Number number, Range range)
{
return (float) clamp_double(number, range.getMin(), range.getMax());
}
/**
* Clamp number to min and infinite bounds, inclusive.<br>
* DOUBLE version
*
* @param number clamped number
* @param min minimal allowed value
* @return result
*/
public static double clampd(Number number, Number min)
{
return clamp_double(number, min);
}
/**
* Clamp number to min and infinite bounds, inclusive.<br>
* FLOAT version
*
* @param number clamped number
* @param min minimal allowed value
* @return result
*/
public static float clampf(Number number, Number min)
{
return (float) clamp_double(number, min);
}
/**
* Clamp number to min and infinite bounds, inclusive.<br>
* INTEGER version
*
* @param number clamped number
* @param min minimal allowed value
* @return result
*/
public static int clampi(Number number, Number min)
{
return (int) Math.round(clamp_double(number, min));
}
/**
* Get class simple name
*
* @param obj object
* @return simple name
*/
public static String cname(Object obj)
{
if (obj == null) return "NULL";
return obj.getClass().getSimpleName();
}
/**
* Cube a double
*
* @param a squared double
* @return square
*/
public static double cube(double a)
{
return a * a * a;
}
/**
* Convert double to string, remove the mess at the end.
*
* @param d double
* @return string
*/
public static String toString(double d)
{
String s = Double.toString(d);
s = s.replace(',', '.');
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
s = s.replaceAll("0+$", "");
s = s.replaceAll("\\.$", "");
return s;
}
/**
* Convert float to string, remove the mess at the end.
*
* @param f float
* @return string
*/
public static String toString(float f)
{
String s = Float.toString(f);
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
s = s.replaceAll("0+$", "");
s = s.replaceAll("\\.$", "");
return s;
}
/**
* Check if number is in range
*
* @param number checked
* @param left lower end
* @param right upper end
* @return is in range
*/
public static boolean inRange(double number, double left, double right)
{
return number >= left && number <= right;
}
/**
* Get number from A to B at delta time (tween A to B)
*
* @param from last number
* @param to new number
* @param time time 0..1
* @param easing easing function
* @return current number to render
*/
public static double interpolate(double from, double to, double time, Easing easing)
{
return from + (to - from) * easing.get(time);
}
/**
* Get angle [degrees] from A to B at delta time (tween A to B)
*
* @param from last angle
* @param to new angle
* @param time time 0..1
* @param easing easing function
* @return current angle to render
*/
public static double interpolateDeg(double from, double to, double time, Easing easing)
{
return Deg.norm(from - Deg.delta(to, from) * easing.get(time));
}
/**
* Get angle [radians] from A to B at delta time (tween A to B)
*
* @param from last angle
* @param to new angle
* @param time time 0..1
* @param easing easing function
* @return current angle to render
*/
public static double interpolateRad(double from, double to, double time, Easing easing)
{
return Rad.norm(from - Rad.delta(to, from) * easing.get(time));
}
/**
* Get highest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static double max(double... numbers)
{
double highest = numbers[0];
for (final double num : numbers) {
if (num > highest) highest = num;
}
return highest;
}
/**
* Get highest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static float max(float... numbers)
{
float highest = numbers[0];
for (final float num : numbers) {
if (num > highest) highest = num;
}
return highest;
}
/**
* Get highest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static int max(int... numbers)
{
int highest = numbers[0];
for (final int num : numbers) {
if (num > highest) highest = num;
}
return highest;
}
/**
* Get lowest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static double min(double... numbers)
{
double lowest = numbers[0];
for (final double num : numbers) {
if (num < lowest) lowest = num;
}
return lowest;
}
/**
* Get lowest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static float min(float... numbers)
{
float lowest = numbers[0];
for (final float num : numbers) {
if (num < lowest) lowest = num;
}
return lowest;
}
/**
* Get lowest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static int min(int... numbers)
{
int lowest = numbers[0];
for (final int num : numbers) {
if (num < lowest) lowest = num;
}
return lowest;
}
/**
* Split comma separated list of integers.
*
* @param list String containing the list.
* @return array of integers or null.
*/
public static List<Integer> parseIntList(String list)
{
if (list == null) {
return null;
}
final String[] parts = list.split(",");
final ArrayList<Integer> intList = new ArrayList<>();
for (final String part : parts) {
try {
intList.add(Integer.parseInt(part));
} catch (final NumberFormatException e) {}
}
return intList;
}
/**
* Pick random element from a given list.
*
* @param list list of choices
* @return picked element
*/
public static Object pick(List<?> list)
{
if (list.size() == 0) return null;
return list.get(rand.nextInt(list.size()));
}
/**
* Square a double
*
* @param a squared double
* @return square
*/
public static double square(double a)
{
return a * a;
}
/**
* Signum.
*
* @param number
* @return sign, -1,0,1
*/
public static int sgn(double number)
{
return number > 0 ? 1 : number < 0 ? -1 : 0;
}
public static double frag(double d)
{
return d - Math.floor(d);
}
}
+317
View File
@@ -0,0 +1,317 @@
package mightypork.util.math;
import mightypork.util.annotations.FactoryMethod;
/**
* EasingFunction function.
*
* @author MightyPork
*/
public abstract class Easing {
/**
* Get value at time t.
*
* @param t time parameter (t = 1..1)
* @return value at given t (0..1, can exceed if needed)
*/
public abstract double get(double t);
/**
* Reverse an easing
*
* @param original original easing
* @return reversed easing
*/
@FactoryMethod
public static Easing reverse(Easing original)
{
return new Reverse(original);
}
/**
* Combine two easings
*
* @param in initial easing
* @param out terminal easing
* @return product
*/
@FactoryMethod
public static Easing combine(Easing in, Easing out)
{
return new Composite(in, out);
}
/**
* Create "bilinear" easing - compose of straight and reverse.
*
* @param in initial easing
* @return product
*/
@FactoryMethod
public static Easing inOut(Easing in)
{
return combine(in, reverse(in));
}
/**
* Reverse EasingFunction
*
* @author MightyPork
*/
private static class Reverse extends Easing {
private final Easing ea;
/**
* @param in Easing to reverse
*/
public Reverse(Easing in) {
this.ea = in;
}
@Override
public double get(double t)
{
return 1 - ea.get(1 - t);
}
}
/**
* Composite EasingFunction (0-0.5 EasingFunction A, 0.5-1 EasingFunction B)
*
* @author MightyPork
*/
private static class Composite extends Easing {
private final Easing in;
private final Easing out;
/**
* Create a composite EasingFunction
*
* @param in initial EasingFunction
* @param out terminal EasingFunction
*/
public Composite(Easing in, Easing out) {
this.in = in;
this.out = out;
}
@Override
public double get(double t)
{
if (t < 0.5) return in.get(2 * t) * 0.5;
return 0.5 + out.get(2 * t - 1) * 0.5;
}
}
/** No easing; At t=0.5 goes high. */
public static final Easing NONE = new Easing() {
@Override
public double get(double t)
{
return (t < 0.5 ? 0 : 1);
}
};
/** Linear (y=t) easing */
public static final Easing LINEAR = new Easing() {
@Override
public double get(double t)
{
return t;
}
};
/** Quadratic (y=t^2) easing in */
public static final Easing QUADRATIC_IN = new Easing() {
@Override
public double get(double t)
{
return t * t;
}
};
/** Quadratic (y=t^2) easing out */
public static final Easing QUADRATIC_OUT = reverse(QUADRATIC_IN);
/** Quadratic (y=t^2) easing both */
public static final Easing QUADRATIC_BOTH = inOut(QUADRATIC_IN);
/** Cubic (y=t^3) easing in */
public static final Easing CUBIC_IN = new Easing() {
@Override
public double get(double t)
{
return t * t * t;
}
};
/** Cubic (y=t^3) easing out */
public static final Easing CUBIC_OUT = reverse(CUBIC_IN);
/** Cubic (y=t^3) easing both */
public static final Easing CUBIC_BOTH = inOut(CUBIC_IN);
/** Quartic (y=t^4) easing in */
public static final Easing QUARTIC_IN = new Easing() {
@Override
public double get(double t)
{
return t * t * t * t;
}
};
/** Quartic (y=t^4) easing out */
public static final Easing QUARTIC_OUT = reverse(QUADRATIC_IN);
/** Quartic (y=t^4) easing both */
public static final Easing QUARTIC_BOTH = inOut(QUADRATIC_IN);
/** Quintic (y=t^5) easing in */
public static final Easing QUINTIC_IN = new Easing() {
@Override
public double get(double t)
{
return t * t * t * t * t;
}
};
/** Quintic (y=t^5) easing out */
public static final Easing QUINTIC_OUT = reverse(QUINTIC_IN);
/** Quintic (y=t^5) easing both */
public static final Easing QUINTIC_BOTH = inOut(QUINTIC_IN);
/** Sine easing in */
public static final Easing SINE_IN = new Easing() {
@Override
public double get(double t)
{
return 1 - Math.cos(t * (Math.PI / 2));
}
};
/** Sine easing out */
public static final Easing SINE_OUT = reverse(SINE_IN);
/** Sine easing both */
public static final Easing SINE_BOTH = inOut(SINE_IN);
/** Exponential easing in */
public static final Easing EXPO_IN = new Easing() {
@Override
public double get(double t)
{
return Math.pow(2, 10 * (t - 1));
}
};
/** Exponential easing out */
public static final Easing EXPO_OUT = reverse(EXPO_IN);
/** Exponential easing both */
public static final Easing EXPO_BOTH = inOut(EXPO_IN);
/** Circular easing in */
public static final Easing CIRC_IN = new Easing() {
@Override
public double get(double t)
{
return 1 - Math.sqrt(1 - t * t);
}
};
/** Circular easing out */
public static final Easing CIRC_OUT = reverse(CIRC_IN);
/** Circular easing both */
public static final Easing CIRC_BOTH = inOut(CIRC_IN);
/** Bounce easing in */
public static final Easing BOUNCE_OUT = new Easing() {
@Override
public double get(double t)
{
if (t < (1 / 2.75f)) {
return (7.5625f * t * t);
} else if (t < (2 / 2.75f)) {
t -= (1.5f / 2.75f);
return (7.5625f * t * t + 0.75f);
} else if (t < (2.5 / 2.75)) {
t -= (2.25f / 2.75f);
return (7.5625f * t * t + 0.9375f);
} else {
t -= (2.625f / 2.75f);
return (7.5625f * t * t + 0.984375f);
}
}
};
/** Bounce easing out */
public static final Easing BOUNCE_IN = reverse(BOUNCE_OUT);
/** Bounce easing both */
public static final Easing BOUNCE_BOTH = inOut(BOUNCE_IN);
/** Back easing in */
public static final Easing BACK_IN = new Easing() {
@Override
public double get(double t)
{
final float s = 1.70158f;
return t * t * ((s + 1) * t - s);
}
};
/** Back easing out */
public static final Easing BACK_OUT = reverse(BACK_IN);
/** Back easing both */
public static final Easing BACK_BOTH = inOut(BACK_IN);
/** Elastic easing in */
public static final Easing ELASTIC_IN = new Easing() {
@Override
public double get(double t)
{
if (t == 0) return 0;
if (t == 1) return 1;
final double p = .3f;
final double s = p / 4;
return -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
}
};
/** Elastic easing out */
public static final Easing ELASTIC_OUT = reverse(ELASTIC_IN);
/** Elastic easing both */
public static final Easing ELASTIC_BOTH = inOut(ELASTIC_IN);
}
+190
View File
@@ -0,0 +1,190 @@
package mightypork.util.math;
import mightypork.util.math.constraints.vect.Vect;
/**
* Polar coordinate
*
* @author MightyPork
*/
public class Polar {
/** angle in radians */
private double angle = 0;
/** distance in units */
private double radius = 0;
private Vect coord = null;
/**
* Create a polar
*
* @param angle angle in RAD
* @param distance distance from origin
*/
public Polar(double angle, double distance) {
this(angle, false, distance);
}
/**
* Create a polar
*
* @param angle angle
* @param deg angle is in DEG
* @param distance radius
*/
public Polar(double angle, boolean deg, double distance) {
this.radius = distance;
this.angle = deg ? Math.toRadians(angle) : angle;
}
/**
* @return angle in RAD
*/
public double getAngle()
{
return angle;
}
/**
* @return angle in DEG
*/
public double getAngleDeg()
{
return Math.toDegrees(angle);
}
/**
* @param angle angle in RAD
*/
public void setAngle(double angle)
{
this.angle = angle;
}
/**
* @param angle angle in DEG
*/
public void setAngleDeg(double angle)
{
this.angle = Math.toRadians(angle);
}
/**
* @return radius
*/
public double getRadius()
{
return radius;
}
/**
* @param r radius
*/
public void setRadius(double r)
{
this.radius = r;
}
/**
* Make polar from coord
*
* @param coord coord
* @return polar
*/
public static Polar fromCoord(Vect coord)
{
return Polar.fromCoord(coord.x(), coord.y());
}
/**
* Make polar from coords
*
* @param x x coord
* @param y y coord
* @return polar
*/
public static Polar fromCoord(double x, double y)
{
final double a = Math.atan2(y, x);
final double r = Math.sqrt(x * x + y * y);
return new Polar(a, r);
}
/**
* Get coord from polar
*
* @return coord
*/
public Vect toCoord()
{
// lazy init
if (coord == null) coord = new Vect() {
@Override
public double x()
{
return radius * Math.cos(angle);
}
@Override
public double y()
{
return radius * Math.sin(angle);
}
};
return coord;
}
@Override
public String toString()
{
return "Polar(" + angle + "rad, " + radius + ")";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(angle);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(radius);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof Polar)) return false;
final Polar other = (Polar) obj;
if (Double.doubleToLongBits(angle) != Double.doubleToLongBits(other.angle)) return false;
if (Double.doubleToLongBits(radius) != Double.doubleToLongBits(other.radius)) return false;
return true;
}
}
+182
View File
@@ -0,0 +1,182 @@
package mightypork.util.math;
import java.util.Random;
/**
* Numeric range, able to generate random numbers and give min/max values.
*
* @author MightyPork
*/
public class Range {
public static Range make(double low, double high)
{
return new Range(low, high);
}
private double min = 0;
private double max = 1;
private static Random rand = new Random();
/**
* Implicit range constructor 0-1
*/
public Range() {
}
/**
* Create new range
*
* @param min min number
* @param max max number
*/
public Range(double min, double max) {
this.min = min;
this.max = max;
norm();
}
/**
* Create new range
*
* @param minmax min = max number
*/
public Range(double minmax) {
this.min = minmax;
this.max = minmax;
}
/**
* Make sure min is <= max
*/
private void norm()
{
if (min > max) {
final double t = min;
min = max;
max = t;
}
}
/**
* Get random integer from range
*
* @return random int
*/
public int randInt()
{
return (int) (Math.round(min) + rand.nextInt((int) (Math.round(max) - Math.round(min)) + 1));
}
/**
* Get random double from this range
*
* @return random double
*/
public double randDouble()
{
return min + rand.nextDouble() * (max - min);
}
/**
* Get min
*
* @return min number
*/
public double getMin()
{
return min;
}
/**
* Get max
*
* @return max number
*/
public double getMax()
{
return max;
}
/**
* Set min
*
* @param min min value
*/
public void setMin(double min)
{
this.min = min;
norm();
}
/**
* Set max
*
* @param max max value
*/
public void setMax(double max)
{
this.max = max;
norm();
}
@Override
public String toString()
{
return String.format("{%.1f|%.1f}", min, max);
}
/**
* Get identical copy
*
* @return copy
*/
public Range copy()
{
return new Range(min, max);
}
/**
* Set to value of other range
*
* @param other copied range
*/
public void setTo(Range other)
{
if (other == null) return;
min = other.min;
max = other.max;
norm();
}
/**
* Set to min-max values
*
* @param min min value
* @param max max value
*/
public void setTo(double min, double max)
{
this.min = min;
this.max = max;
norm();
}
}
+212
View File
@@ -0,0 +1,212 @@
package mightypork.util.math.color;
import java.util.Stack;
import mightypork.util.annotations.FactoryMethod;
import mightypork.util.math.Calc;
import mightypork.util.math.constraints.num.Num;
/**
* Color.<br>
* All values are 0-1
*
* @author MightyPork
*/
public abstract class Color {
public static final Color NONE = rgba(0, 0, 0, 0);
public static final Color SHADOW = rgba(0, 0, 0, 0.5);
public static final Color WHITE = rgb(1, 1, 1);
public static final Color BLACK = rgb(0, 0, 0);
public static final Color DARK_GRAY = rgb(0.25, 0.25, 0.25);
public static final Color GRAY = rgb(0.5, 0.5, 0.5);
public static final Color LIGHT_GRAY = rgb(0.75, 0.75, 0.75);
public static final Color RED = rgb(1, 0, 0);
public static final Color GREEN = rgb(0, 1, 0);
public static final Color BLUE = rgb(0, 0, 1);
public static final Color YELLOW = rgb(1, 1, 0);
public static final Color MAGENTA = rgb(1, 0, 1);
public static final Color CYAN = rgb(0, 1, 1);
public static final Color ORANGE = rgb(1, 0.78, 0);
public static final Color PINK = rgb(1, 0.68, 0.68);
private static final Stack<Num> globalAlphaStack = new Stack<>();
@FactoryMethod
public static final Color rgb(double r, double g, double b)
{
return rgba(Num.make(r), Num.make(g), Num.make(b), Num.ONE);
}
@FactoryMethod
public static final Color rgba(double r, double g, double b, double a)
{
return rgba(Num.make(r), Num.make(g), Num.make(b), Num.make(a));
}
@FactoryMethod
public static final Color rgba(Num r, Num g, Num b)
{
return rgba(r, g, b, Num.ONE);
}
@FactoryMethod
public static final Color rgba(Num r, Num g, Num b, Num a)
{
return new ColorRgb(r, g, b, a);
}
@FactoryMethod
public static final Color hsb(double h, double s, double b)
{
return hsba(Num.make(h), Num.make(s), Num.make(b), Num.ONE);
}
@FactoryMethod
public static final Color hsba(double h, double s, double b, double a)
{
return hsba(Num.make(h), Num.make(s), Num.make(b), Num.make(a));
}
@FactoryMethod
public static final Color hsb(Num h, Num s, Num b)
{
return hsba(h, s, b, Num.ONE);
}
@FactoryMethod
public static final Color hsba(Num h, Num s, Num b, Num a)
{
return new ColorHsb(h, s, b, a);
}
@FactoryMethod
public static final Color light(double a)
{
return light(Num.make(a));
}
@FactoryMethod
public static final Color light(Num a)
{
return rgba(Num.ONE, Num.ONE, Num.ONE, a);
}
@FactoryMethod
public static final Color dark(double a)
{
return dark(Num.make(a));
}
@FactoryMethod
public static final Color dark(Num a)
{
return rgba(Num.ZERO, Num.ZERO, Num.ZERO, a);
}
protected static final double clamp(Num n)
{
return Calc.clampd(n.value(), 0, 1);
}
protected static final double clamp(double n)
{
return Calc.clampd(n, 0, 1);
}
/**
* @return red 0-1
*/
public abstract double red();
/**
* @return green 0-1
*/
public abstract double green();
/**
* @return blue 0-1
*/
public abstract double blue();
/**
* @return alpha 0-1
*/
public final double alpha()
{
double alpha = rawAlpha();
for (Num n : globalAlphaStack) {
alpha *= clamp(n.value());
}
return clamp(alpha);
}
/**
* @return alpha 0-1, before multiplying with the global alpha value.
*/
protected abstract double rawAlpha();
/**
* <p>
* Push alpha multiplier on the stack (can be animated or whatever you
* like). Once done with rendering, the popAlpha() method should be called,
* otherwise you may experience unexpected glitches (such as all going
* transparent).
* </p>
* <p>
* multiplier value should not exceed the range 0..1, otherwise it will be
* clamped to it.
* </p>
*
* @param alpha alpha multiplier
*/
public static void pushAlpha(Num alpha)
{
globalAlphaStack.push(alpha);
}
/**
* Remove a pushed alpha multiplier from the stack. If there's no remaining
* multiplier on the stack, an exception is raised.
*
* @return the popped multiplier
* @throws IllegalStateException if the stack is empty
*/
public static Num popAlpha()
{
if (globalAlphaStack.isEmpty()) {
throw new IllegalStateException("Global alpha stack underflow.");
}
return globalAlphaStack.pop();
}
}
@@ -0,0 +1,61 @@
package mightypork.util.math.color;
import mightypork.util.math.constraints.num.Num;
public class ColorHsb extends Color {
private final Num h;
private final Num s;
private final Num b;
private final Num a;
public ColorHsb(Num h, Num s, Num b, Num a) {
this.h = h;
this.s = s;
this.b = b;
this.a = a;
}
private double[] asRgb()
{
final int hex = java.awt.Color.HSBtoRGB((float) clamp(h), (float) clamp(s), (float) clamp(b));
final int bi = hex & 0xff;
final int gi = (hex >> 8) & 0xff;
final int ri = (hex >> 16) & 0xff;
return new double[] { ri / 255D, gi / 255D, bi / 255D, clamp(a) };
}
@Override
public double red()
{
return asRgb()[0];
}
@Override
public double green()
{
return asRgb()[1];
}
@Override
public double blue()
{
return asRgb()[2];
}
@Override
public double rawAlpha()
{
return asRgb()[3];
}
}
@@ -0,0 +1,50 @@
package mightypork.util.math.color;
import mightypork.util.math.constraints.num.Num;
public class ColorRgb extends Color {
private final Num r;
private final Num g;
private final Num b;
private final Num a;
public ColorRgb(Num r, Num g, Num b, Num a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
@Override
public double red()
{
return clamp(r);
}
@Override
public double green()
{
return clamp(g);
}
@Override
public double blue()
{
return clamp(b);
}
@Override
public double rawAlpha()
{
return clamp(a);
}
}
@@ -0,0 +1,44 @@
package mightypork.util.math.constraints;
/**
* Constraint cache
*
* @author MightyPork
* @param <C> constraint type
*/
public interface ConstraintCache<C> extends Pollable {
/**
* Called after the cache has changed value (and digest).
*/
void onChange();
/**
* @return the cached value
*/
C getCacheSource();
/**
* Enable caching & digest caching
*
* @param yes enable caching
*/
void enableCaching(boolean yes);
/**
* @return true if caching is on
*/
boolean isCachingEnabled();
/**
* Update cached value and cached digest (if digest caching is enabled).<br>
* source constraint is polled beforehand.
*/
@Override
void poll();
}
@@ -0,0 +1,60 @@
package mightypork.util.math.constraints;
/**
* Parametrized implementation of a {@link Digestable}
*
* @author MightyPork
* @param <D> digest class
*/
public abstract class DigestCache<D> implements Digestable<D> {
private D last_digest;
private boolean caching_enabled;
private boolean dirty = true;
@Override
public final D digest()
{
if (caching_enabled) {
if (dirty || last_digest == null) {
last_digest = createDigest();
dirty = false;
}
return last_digest;
}
return createDigest();
}
/**
* @return fresh new digest
*/
protected abstract D createDigest();
@Override
public final void enableDigestCaching(boolean yes)
{
caching_enabled = yes;
markDigestDirty(); // mark dirty
}
@Override
public final boolean isDigestCachingEnabled()
{
return caching_enabled;
}
@Override
public final void markDigestDirty()
{
dirty = true;
}
}
@@ -0,0 +1,60 @@
package mightypork.util.math.constraints;
/**
* <p>
* Interface for constraints that support digests. Digest is a small data object
* with final fields, typically primitive, used for processing (such as
* rendering or other very frequent operations).
* </p>
* <p>
* Taking a digest is expensive, so if it needs to be done often and the value
* changes are deterministic (such as, triggered by timing event or screen
* resize), it's useful to cache the last digest and reuse it until such an
* event occurs again.
* </p>
* <p>
* Implementing class typically needs a field to store the last digest, a flag
* that digest caching is enabled, and a flag that a digest is dirty.
* </p>
*
* @author MightyPork
* @param <D> digest class
*/
public interface Digestable<D> {
/**
* Take a digest. If digest caching is enabled and the cached digest is
* marked as dirty, a new one should be made.
*
* @return digest
*/
public D digest();
/**
* <p>
* Toggle digest caching.
* </p>
* <p>
* To trigger update of the cache, call the <code>poll()</code> method.
* </p>
*
* @param yes
*/
void enableDigestCaching(boolean yes);
/**
* @return true if digest caching is enabled.
*/
boolean isDigestCachingEnabled();
/**
* If digest caching is enabled, mark current cached value as "dirty". Dirty
* digest should be re-created next time a value is requested.<br>
*/
void markDigestDirty();
}
@@ -0,0 +1,15 @@
package mightypork.util.math.constraints;
/**
* Can be asked to update it's state
*
* @author MightyPork
*/
public interface Pollable {
/**
* Update internal state
*/
void poll();
}
@@ -0,0 +1,47 @@
package mightypork.util.math.constraints;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Used to poll a number of {@link Pollable}s
*
* @author MightyPork
*/
public class Poller implements Pollable {
private final Set<Pollable> pollables = new LinkedHashSet<>();
/**
* Add a pollable
*
* @param p pollable
*/
public void add(Pollable p)
{
pollables.add(p);
}
/**
* Remove a pollalbe
*
* @param p pollable
*/
public void remove(Pollable p)
{
pollables.remove(p);
}
@Override
public void poll()
{
for (final Pollable p : pollables) {
p.poll();
}
}
}
@@ -0,0 +1,750 @@
package mightypork.util.math.constraints.num;
import mightypork.util.annotations.FactoryMethod;
import mightypork.util.math.Calc;
import mightypork.util.math.constraints.DigestCache;
import mightypork.util.math.constraints.Digestable;
import mightypork.util.math.constraints.num.caching.NumCache;
import mightypork.util.math.constraints.num.caching.NumDigest;
import mightypork.util.math.constraints.num.mutable.NumVar;
import mightypork.util.math.constraints.num.proxy.NumBound;
import mightypork.util.math.constraints.num.proxy.NumBoundAdapter;
public abstract class Num implements NumBound, Digestable<NumDigest> {
public static final NumConst ZERO = Num.make(0);
public static final NumConst ONE = Num.make(1);
@FactoryMethod
public static Num make(NumBound bound)
{
return new NumBoundAdapter(bound);
}
@FactoryMethod
public static NumConst make(double value)
{
return new NumConst(value);
}
@FactoryMethod
public static NumVar makeVar()
{
return makeVar(0);
}
@FactoryMethod
public static NumVar makeVar(double value)
{
return new NumVar(value);
}
@FactoryMethod
public static NumVar makeVar(Num copied)
{
return new NumVar(copied.value());
}
private Num p_ceil;
private Num p_floor;
private Num p_sgn;
private Num p_round;
private Num p_atan;
private Num p_acos;
private Num p_asin;
private Num p_tan;
private Num p_cos;
private Num p_sin;
private Num p_cbrt;
private Num p_sqrt;
private Num p_cube;
private Num p_square;
private Num p_neg;
private Num p_abs;
private DigestCache<NumDigest> dc = new DigestCache<NumDigest>() {
@Override
protected NumDigest createDigest()
{
return new NumDigest(Num.this);
}
};
public NumConst freeze()
{
return new NumConst(value());
}
/**
* Wrap this constraint into a caching adapter. Value will stay fixed (ie.
* no re-calculations) until cache receives a poll() call.
*
* @return the caching adapter
*/
public NumCache cached()
{
return new NumCache(this);
}
/**
* Get a snapshot of the current state, to be used for processing.
*
* @return digest
*/
@Override
public NumDigest digest()
{
return dc.digest();
}
@Override
public void enableDigestCaching(boolean yes)
{
dc.enableDigestCaching(yes);
}
@Override
public boolean isDigestCachingEnabled()
{
return dc.isDigestCachingEnabled();
}
@Override
public void markDigestDirty()
{
dc.markDigestDirty();
}
@Override
public Num getNum()
{
return this;
}
/**
* @return the number
*/
public abstract double value();
public Num add(final double addend)
{
return new Num() {
private final Num t = Num.this;
@Override
public double value()
{
return t.value() + addend;
}
};
}
public Num add(final Num addend)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return t.value() + addend.value();
}
};
}
public Num sub(final double subtrahend)
{
return add(-subtrahend);
}
public Num abs()
{
if (p_abs == null) p_abs = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.abs(t.value());
}
};
return p_abs;
}
public Num sub(final Num subtrahend)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return t.value() - subtrahend.value();
}
};
}
public Num div(final double factor)
{
return mul(1 / factor);
}
public Num div(final Num factor)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return t.value() / factor.value();
}
};
}
public Num mul(final double factor)
{
return new Num() {
private final Num t = Num.this;
@Override
public double value()
{
return t.value() * factor;
}
};
}
public Num mul(final Num factor)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return t.value() * factor.value();
}
};
}
public Num average(final double other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return (t.value() + other) / 2;
}
};
}
public Num average(final Num other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return (t.value() + other.value()) / 2;
}
};
}
public Num perc(final double percent)
{
return mul(percent / 100);
}
public Num perc(final Num percent)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return t.value() * (percent.value() / 100);
}
};
}
public Num cos()
{
if (p_cos == null) p_cos = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.cos(t.value());
}
};
return p_cos;
}
public Num acos()
{
if (p_acos == null) p_acos = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.acos(t.value());
}
};
return p_acos;
}
public Num sin()
{
if (p_sin == null) p_sin = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.sin(t.value());
}
};
return p_sin;
}
public Num asin()
{
if (p_asin == null) p_asin = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.asin(t.value());
}
};
return p_asin;
}
public Num tan()
{
if (p_tan == null) p_tan = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.tan(t.value());
}
};
return p_tan;
}
public Num atan()
{
if (p_atan == null) p_atan = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.atan(t.value());
}
};
return p_atan;
}
public Num cbrt()
{
if (p_cbrt == null) p_cbrt = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.cbrt(t.value());
}
};
return p_cbrt;
}
public Num sqrt()
{
if (p_sqrt == null) p_sqrt = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.sqrt(t.value());
}
};
return p_sqrt;
}
public Num neg()
{
if (p_neg == null) p_neg = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return -1 * t.value();
}
};
return p_neg;
}
public Num round()
{
if (p_round == null) p_round = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.round(t.value());
}
};
return p_round;
}
public Num floor()
{
if (p_floor == null) p_floor = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.floor(t.value());
}
};
return p_floor;
}
public Num ceil()
{
if (p_ceil == null) p_ceil = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.round(t.value());
}
};
return p_ceil;
}
public Num pow(final double other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.pow(t.value(), other);
}
};
}
public Num pow(final Num power)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.pow(t.value(), power.value());
}
};
}
public Num cube()
{
if (p_cube == null) p_cube = new Num() {
final Num t = Num.this;
@Override
public double value()
{
final double v = t.value();
return v * v * v;
}
};
return p_cube;
}
public Num square()
{
if (p_square == null) p_square = new Num() {
final Num t = Num.this;
@Override
public double value()
{
final double v = t.value();
return v * v;
}
};
return p_square;
}
public Num half()
{
return mul(0.5);
}
public Num max(final double other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.max(t.value(), other);
}
};
}
public Num max(final Num other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.max(t.value(), other.value());
}
};
}
public Num min(final Num other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.min(t.value(), other.value());
}
};
}
public Num min(final double other)
{
return new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.min(t.value(), other);
}
};
}
public Num signum()
{
if (p_sgn == null) p_sgn = new Num() {
final Num t = Num.this;
@Override
public double value()
{
return Math.signum(t.value());
}
};
return p_sgn;
}
public boolean isNegative()
{
return value() < 0;
}
public boolean isPositive()
{
return value() > 0;
}
public boolean isZero()
{
return value() == 0;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(value());
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof Num)) return false;
final Num other = (Num) obj;
return value() == other.value();
}
@Override
public String toString()
{
return Calc.toString(value());
}
}
@@ -0,0 +1,268 @@
package mightypork.util.math.constraints.num;
import mightypork.util.math.constraints.num.caching.NumDigest;
/**
* Constant number.<br>
* It's arranged so that operations with constant arguments yield constant
* results.
*
* @author MightyPork
*/
public class NumConst extends Num {
private final double value;
private NumDigest digest;
NumConst(Num copied) {
this.value = copied.value();
}
NumConst(double value) {
this.value = value;
}
@Override
public double value()
{
return value;
}
/**
* @deprecated No good to copy a constant.
*/
@Override
@Deprecated
public NumConst freeze()
{
return this;
}
@Override
public NumDigest digest()
{
return (digest != null) ? digest : (digest = super.digest());
}
@Override
public NumConst add(double addend)
{
return Num.make(value() + addend);
}
public NumConst add(NumConst addend)
{
return Num.make(value + addend.value);
}
@Override
public NumConst sub(double subtrahend)
{
return add(-subtrahend);
}
public NumConst sub(NumConst addend)
{
return Num.make(value - addend.value);
}
@Override
public NumConst mul(double factor)
{
return Num.make(value() * factor);
}
public NumConst mul(NumConst addend)
{
return Num.make(value * addend.value);
}
@Override
public NumConst div(double factor)
{
return mul(1 / factor);
}
public NumConst div(NumConst addend)
{
return Num.make(value / addend.value);
}
@Override
public NumConst perc(double percents)
{
return mul(percents / 100);
}
@Override
public NumConst neg()
{
return mul(-1);
}
@Override
public NumConst abs()
{
return Num.make(Math.abs(value()));
}
@Override
public NumConst max(double other)
{
return Num.make(Math.max(value(), other));
}
@Override
public NumConst min(double other)
{
return Num.make(Math.min(value(), other));
}
@Override
public NumConst pow(double power)
{
return Num.make(Math.pow(value(), power));
}
@Override
public NumConst square()
{
final double v = value();
return Num.make(v * v);
}
@Override
public NumConst cube()
{
final double v = value();
return Num.make(v * v * v);
}
@Override
public NumConst sqrt()
{
return Num.make(Math.sqrt(value()));
}
@Override
public NumConst cbrt()
{
return Num.make(Math.cbrt(value()));
}
@Override
public NumConst sin()
{
return Num.make(Math.sin(value()));
}
@Override
public NumConst cos()
{
return Num.make(Math.cos(value()));
}
@Override
public NumConst tan()
{
return Num.make(Math.tan(value()));
}
@Override
public NumConst asin()
{
return Num.make(Math.asin(value()));
}
@Override
public NumConst acos()
{
return Num.make(Math.acos(value()));
}
@Override
public NumConst atan()
{
return Num.make(Math.atan(value()));
}
@Override
public NumConst signum()
{
return Num.make(Math.signum(value()));
}
@Override
public NumConst average(double other)
{
return Num.make((value() + other) / 2);
}
public NumConst average(NumConst other)
{
return super.average(other).freeze();
}
@Override
public NumConst round()
{
return Num.make(Math.round(value()));
}
@Override
public NumConst ceil()
{
return Num.make(Math.ceil(value()));
}
@Override
public NumConst floor()
{
return Num.make(Math.floor(value()));
}
@Override
public NumConst half()
{
return mul(0.5);
}
}
@@ -0,0 +1,83 @@
package mightypork.util.math.constraints.num.caching;
import mightypork.util.math.constraints.ConstraintCache;
import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.num.mutable.NumVar;
import mightypork.util.math.constraints.num.proxy.NumAdapter;
/**
* <p>
* A Num cache.
* </p>
* <p>
* Values are held in a caching VectVar, and digest caching is enabled by
* default.
* </p>
*
* @author MightyPork
*/
public abstract class AbstractNumCache extends NumAdapter implements ConstraintCache<Num> {
private final NumVar cache = Num.makeVar();
private boolean inited = false;
private boolean cachingEnabled = true;
public AbstractNumCache() {
enableDigestCaching(true); // it changes only on poll
}
@Override
protected final Num getSource()
{
if (!inited) markDigestDirty();
return (cachingEnabled ? cache : getCacheSource());
}
@Override
public final void poll()
{
inited = true;
// poll source
final Num source = getCacheSource();
source.markDigestDirty(); // poll cached
// store source value
cache.setTo(source);
// mark my digest dirty
markDigestDirty();
onChange();
}
@Override
public abstract void onChange();
@Override
public abstract Num getCacheSource();
@Override
public final void enableCaching(boolean yes)
{
cachingEnabled = yes;
enableDigestCaching(yes);
}
@Override
public final boolean isCachingEnabled()
{
return cachingEnabled;
}
}
@@ -0,0 +1,36 @@
package mightypork.util.math.constraints.num.caching;
import mightypork.util.annotations.DefaultImpl;
import mightypork.util.math.constraints.num.Num;
/**
* Num cache implementation
*
* @author MightyPork
*/
public class NumCache extends AbstractNumCache {
private final Num source;
public NumCache(Num source) {
this.source = source;
}
@Override
public final Num getCacheSource()
{
return source;
}
@Override
@DefaultImpl
public void onChange()
{
}
}
@@ -0,0 +1,22 @@
package mightypork.util.math.constraints.num.caching;
import mightypork.util.math.constraints.num.Num;
public class NumDigest {
public final double value;
public NumDigest(Num num) {
this.value = num.value();
}
@Override
public String toString()
{
return String.format("Num(%.1f)", value);
}
}
@@ -0,0 +1,378 @@
package mightypork.util.math.constraints.num.mutable;
import mightypork.util.control.timing.Pauseable;
import mightypork.util.control.timing.Updateable;
import mightypork.util.math.Calc;
import mightypork.util.math.Easing;
/**
* Double which supports delta timing
*
* @author MightyPork
*/
public class NumAnimated extends NumMutable implements Updateable, Pauseable {
/** target double */
protected double to = 0;
/** last tick double */
protected double from = 0;
/** how long the transition should last */
protected double duration = 0;
/** current anim time */
protected double elapsedTime = 0;
/** True if this animator is paused */
protected boolean paused = false;
/** Easing fn */
protected Easing easing = Easing.LINEAR;
/** Default duration (seconds) */
private double defaultDuration = 0;
/**
* Create linear animator
*
* @param value initial value
*/
public NumAnimated(double value) {
setTo(value);
}
/**
* Create animator with easing
*
* @param value initial value
* @param easing easing function
*/
public NumAnimated(double value, Easing easing) {
this(value);
setEasing(easing);
}
/**
* Create as copy of another
*
* @param other other animator
*/
public NumAnimated(NumAnimated other) {
setTo(other);
}
/**
* @return easing function
*/
public Easing getEasing()
{
return easing;
}
/**
* @param easing easing function
*/
public void setEasing(Easing easing)
{
this.easing = easing;
}
/**
* Get start value
*
* @return number
*/
public double getStart()
{
return from;
}
/**
* Get end value
*
* @return number
*/
public double getEnd()
{
return to;
}
/**
* @return current animation duration (seconds)
*/
public double getDuration()
{
return duration;
}
/**
* @return elapsed time in current animation (seconds)
*/
public double getElapsed()
{
return elapsedTime;
}
/**
* @return default animation duration (seconds)
*/
public double getDefaultDuration()
{
return defaultDuration;
}
/**
* @param defaultDuration default animation duration (seconds)
*/
public void setDefaultDuration(double defaultDuration)
{
this.defaultDuration = defaultDuration;
}
/**
* Get value at delta time
*
* @return the value
*/
@Override
public double value()
{
if (duration == 0) return to;
return Calc.interpolate(from, to, (elapsedTime / duration), easing);
}
/**
* Get how much of the animation is already finished
*
* @return completion ratio (0 to 1)
*/
public double getProgress()
{
if (duration == 0) return 1;
return elapsedTime / duration;
}
@Override
public void update(double delta)
{
if (paused || isFinished()) return;
elapsedTime = Calc.clampd(elapsedTime + delta, 0, duration);
if (isFinished()) {
duration = 0;
elapsedTime = 0;
from = to;
}
}
/**
* Get if animation is finished
*
* @return is finished
*/
public boolean isFinished()
{
return duration == 0 || elapsedTime >= duration;
}
/**
* Set to a value (without animation)
*
* @param value
*/
@Override
public void setTo(double value)
{
from = to = value;
elapsedTime = 0;
duration = defaultDuration;
}
/**
* Copy other
*
* @param other
*/
public void setTo(NumAnimated other)
{
this.from = other.from;
this.to = other.to;
this.duration = other.duration;
this.elapsedTime = other.elapsedTime;
this.paused = other.paused;
this.easing = other.easing;
this.defaultDuration = other.defaultDuration;
}
/**
* Animate between two states, start from current value (if it's in between)
*
* @param from start value
* @param to target state
* @param time animation time (secs)
*/
public void animate(double from, double to, double time)
{
final double current = value();
this.from = from;
this.to = to;
final double progress = getProgressFromValue(current);
this.from = (progress > 0 ? current : from);
this.duration = time * (1 - progress);
this.elapsedTime = 0;
}
/**
* Get progress already elapsed based on current value.<br>
* Used to resume animation from current point in fading etc.
*
* @param value current value
* @return progress ratio 0-1
*/
protected double getProgressFromValue(double value)
{
double p = 0;
if (from == to) return 0;
if (value >= from && value <= to) { // up
p = ((value - from) / (to - from));
} else if (value >= to && value <= from) { // down
p = ((from - value) / (from - to));
}
return p;
}
/**
* Animate to a value from current value
*
* @param to target state
* @param duration animation duration (speeds)
*/
public void animate(double to, double duration)
{
this.from = value();
this.to = to;
this.duration = duration;
this.elapsedTime = 0;
}
/**
* Animate 0 to 1
*
* @param time animation time (secs)
*/
public void fadeIn(double time)
{
animate(0, 1, time);
}
/**
* Animate 1 to 0
*
* @param time animation time (secs)
*/
public void fadeOut(double time)
{
animate(1, 0, time);
}
/**
* Make a copy
*
* @return copy
*/
@Override
public NumAnimated clone()
{
return new NumAnimated(this);
}
@Override
public String toString()
{
return "Animation(" + from + " -> " + to + ", t=" + duration + "s, elapsed=" + elapsedTime + "s)";
}
/**
* Set to zero and stop animation
*/
public void clear()
{
from = to = 0;
elapsedTime = 0;
duration = 0;
paused = false;
}
/**
* Stop animation, keep current value
*/
public void stop()
{
from = to = value();
elapsedTime = 0;
duration = 0;
}
@Override
public void pause()
{
paused = true;
}
@Override
public void resume()
{
paused = false;
}
@Override
public boolean isPaused()
{
return paused;
}
public boolean isInProgress()
{
return !isFinished() && !isPaused();
}
}
@@ -0,0 +1,50 @@
package mightypork.util.math.constraints.num.mutable;
import mightypork.util.math.Calc;
import mightypork.util.math.Calc.Deg;
import mightypork.util.math.Easing;
/**
* Degree animator
*
* @author MightyPork
*/
public class NumAnimatedDeg extends NumAnimated {
public NumAnimatedDeg(NumAnimated other) {
super(other);
}
public NumAnimatedDeg(double value) {
super(value);
}
public NumAnimatedDeg(double value, Easing easing) {
super(value, easing);
}
@Override
public double value()
{
if (duration == 0) return Deg.norm(to);
return Calc.interpolateDeg(from, to, (elapsedTime / duration), easing);
}
@Override
protected double getProgressFromValue(double value)
{
final double whole = Deg.diff(from, to);
if (Deg.diff(value, from) < whole && Deg.diff(value, to) < whole) {
final double partial = Deg.diff(from, value);
return partial / whole;
}
return 0;
}
}
@@ -0,0 +1,50 @@
package mightypork.util.math.constraints.num.mutable;
import mightypork.util.math.Calc;
import mightypork.util.math.Calc.Rad;
import mightypork.util.math.Easing;
/**
* Radians animator
*
* @author MightyPork
*/
public class NumAnimatedRad extends NumAnimated {
public NumAnimatedRad(NumAnimated other) {
super(other);
}
public NumAnimatedRad(double value) {
super(value);
}
public NumAnimatedRad(double value, Easing easing) {
super(value, easing);
}
@Override
public double value()
{
if (duration == 0) return Rad.norm(to);
return Calc.interpolateRad(from, to, (elapsedTime / duration), easing);
}
@Override
protected double getProgressFromValue(double value)
{
final double whole = Rad.diff(from, to);
if (Rad.diff(value, from) < whole && Rad.diff(value, to) < whole) {
final double partial = Rad.diff(from, value);
return partial / whole;
}
return 0;
}
}
@@ -0,0 +1,41 @@
package mightypork.util.math.constraints.num.mutable;
import mightypork.util.math.constraints.num.Num;
/**
* Mutable numeric variable
*
* @author MightyPork
*/
public abstract class NumMutable extends Num {
/**
* Assign a value
*
* @param value new value
*/
public abstract void setTo(double value);
/**
* Assign a value
*
* @param value new value
*/
public void setTo(Num value)
{
setTo(value.value());
}
/**
* Set to zero
*/
public void reset()
{
setTo(0);
}
}
@@ -0,0 +1,40 @@
package mightypork.util.math.constraints.num.mutable;
import mightypork.util.math.constraints.num.Num;
/**
* Mutable numeric variable.
*
* @author MightyPork
*/
public class NumVar extends NumMutable {
private double value;
public NumVar(Num value) {
this(value.value());
}
public NumVar(double value) {
this.value = value;
}
@Override
public double value()
{
return value;
}
@Override
public void setTo(double value)
{
this.value = value;
}
}
@@ -0,0 +1,18 @@
package mightypork.util.math.constraints.num.proxy;
import mightypork.util.math.constraints.num.Num;
public abstract class NumAdapter extends Num {
protected abstract Num getSource();
@Override
public double value()
{
return getSource().value();
}
}
@@ -0,0 +1,19 @@
package mightypork.util.math.constraints.num.proxy;
import mightypork.util.math.constraints.num.Num;
/**
* Numeric constraint
*
* @author MightyPork
*/
public interface NumBound {
/**
* @return current value
*/
Num getNum();
}
@@ -0,0 +1,34 @@
package mightypork.util.math.constraints.num.proxy;
import mightypork.util.math.constraints.num.Num;
public class NumBoundAdapter extends NumAdapter implements PluggableNumBound {
private NumBound backing = null;
public NumBoundAdapter() {
}
public NumBoundAdapter(NumBound bound) {
backing = bound;
}
@Override
public void setNum(NumBound rect)
{
this.backing = rect;
}
@Override
protected Num getSource()
{
return backing.getNum();
}
}
@@ -0,0 +1,23 @@
package mightypork.util.math.constraints.num.proxy;
import mightypork.util.math.constraints.num.Num;
public class NumProxy extends NumAdapter {
private final Num source;
public NumProxy(Num source) {
this.source = source;
}
@Override
protected Num getSource()
{
return source;
}
}
@@ -0,0 +1,16 @@
package mightypork.util.math.constraints.num.proxy;
/**
* Pluggable numeric constraint
*
* @author MightyPork
*/
public interface PluggableNumBound extends NumBound {
/**
* @param num bound to set
*/
abstract void setNum(NumBound num);
}
@@ -0,0 +1,943 @@
package mightypork.util.math.constraints.rect;
import mightypork.util.annotations.FactoryMethod;
import mightypork.util.math.constraints.DigestCache;
import mightypork.util.math.constraints.Digestable;
import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.num.NumConst;
import mightypork.util.math.constraints.rect.builders.TiledRect;
import mightypork.util.math.constraints.rect.caching.RectCache;
import mightypork.util.math.constraints.rect.caching.RectDigest;
import mightypork.util.math.constraints.rect.mutable.RectVar;
import mightypork.util.math.constraints.rect.proxy.RectBound;
import mightypork.util.math.constraints.rect.proxy.RectBoundAdapter;
import mightypork.util.math.constraints.rect.proxy.RectVectAdapter;
import mightypork.util.math.constraints.vect.Vect;
import mightypork.util.math.constraints.vect.VectConst;
/**
* Common methods for all kinds of Rects
*
* @author MightyPork
*/
public abstract class Rect implements RectBound, Digestable<RectDigest> {
public static final RectConst ZERO = new RectConst(0, 0, 0, 0);
public static final RectConst ONE = new RectConst(0, 0, 1, 1);
@FactoryMethod
public static Rect make(Num width, Num height)
{
final Vect origin = Vect.ZERO;
final Vect size = Vect.make(width, height);
return Rect.make(origin, size);
}
public static Rect make(Vect size)
{
return Rect.make(size.xn(), size.yn());
}
@FactoryMethod
public static Rect make(RectBound bound)
{
return new RectBoundAdapter(bound);
}
@FactoryMethod
public static Rect make(Num x, Num y, Num width, Num height)
{
final Vect origin = Vect.make(x, y);
final Vect size = Vect.make(width, height);
return Rect.make(origin, size);
}
@FactoryMethod
public static Rect make(Vect origin, Num width, Num height)
{
return make(origin, Vect.make(width, height));
}
@FactoryMethod
public static Rect make(final Vect origin, final Vect size)
{
return new RectVectAdapter(origin, size);
}
@FactoryMethod
public static RectConst make(NumConst width, NumConst height)
{
final VectConst origin = Vect.ZERO;
final VectConst size = Vect.make(width, height);
return Rect.make(origin, size);
}
public static Rect make(Num side)
{
return make(side, side);
}
public static RectConst make(NumConst side)
{
return make(side, side);
}
public static RectConst make(double side)
{
return make(side, side);
}
@FactoryMethod
public static RectConst make(NumConst x, NumConst y, NumConst width, NumConst height)
{
final VectConst origin = Vect.make(x, y);
final VectConst size = Vect.make(width, height);
return Rect.make(origin, size);
}
@FactoryMethod
public static RectConst make(final VectConst origin, final VectConst size)
{
return new RectConst(origin, size);
}
@FactoryMethod
public static RectConst make(double width, double height)
{
return Rect.make(0, 0, width, height);
}
@FactoryMethod
public static RectConst make(double x, double y, double width, double height)
{
return new RectConst(x, y, width, height);
}
@FactoryMethod
public static RectVar makeVar(double x, double y)
{
return Rect.makeVar(0, 0, x, y);
}
@FactoryMethod
public static RectVar makeVar(Rect copied)
{
return Rect.makeVar(copied.origin(), copied.size());
}
@FactoryMethod
public static RectVar makeVar(Vect origin, Vect size)
{
return Rect.makeVar(origin.x(), origin.y(), size.x(), size.y());
}
@FactoryMethod
public static RectVar makeVar(double x, double y, double width, double height)
{
return new RectVar(x, y, width, height);
}
@FactoryMethod
public static RectVar makeVar()
{
return Rect.makeVar(Rect.ZERO);
}
private Vect p_bl;
private Vect p_bc;
private Vect p_br;
// p_t == origin
private Vect p_tc;
private Vect p_tr;
private Vect p_cl;
private Vect p_cc;
private Vect p_cr;
private Num p_x;
private Num p_y;
private Num p_w;
private Num p_h;
private Num p_l;
private Num p_r;
private Num p_t;
private Num p_b;
private Rect p_edge_l;
private Rect p_edge_r;
private Rect p_edge_t;
private Rect p_edge_b;
private DigestCache<RectDigest> dc = new DigestCache<RectDigest>() {
@Override
protected RectDigest createDigest()
{
return new RectDigest(Rect.this);
}
};
/**
* Get a copy of current value
*
* @return copy
*/
public RectConst freeze()
{
// must NOT call RectVal.make, it'd cause infinite recursion.
return new RectConst(this);
}
/**
* Wrap this constraint into a caching adapter. Value will stay fixed (ie.
* no re-calculations) until cache receives a poll() call.
*
* @return the caching adapter
*/
public RectCache cached()
{
return new RectCache(this);
}
@Override
public RectDigest digest()
{
return dc.digest();
}
@Override
public void enableDigestCaching(boolean yes)
{
dc.enableDigestCaching(yes);
}
@Override
public boolean isDigestCachingEnabled()
{
return dc.isDigestCachingEnabled();
}
@Override
public void markDigestDirty()
{
dc.markDigestDirty();
}
@Override
public Rect getRect()
{
return this;
}
@Override
public String toString()
{
return String.format("Rect { at %s , size %s }", origin(), size());
}
/**
* Origin (top left).
*
* @return origin (top left)
*/
public abstract Vect origin();
/**
* Size (spanning right down from Origin).
*
* @return size vector
*/
public abstract Vect size();
/**
* Add vector to origin
*
* @param move offset vector
* @return result
*/
public Rect move(final Vect move)
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size();
}
@Override
public Vect origin()
{
return t.origin().add(move);
}
};
}
public Rect moveX(Num x)
{
return move(x, Num.ZERO);
}
public Rect moveY(Num y)
{
return move(Num.ZERO, y);
}
public Rect moveX(double x)
{
return move(x, 0);
}
public Rect moveY(double y)
{
return move(0, y);
}
/**
* Add X and Y to origin
*
* @param x x to add
* @param y y to add
* @return result
*/
public Rect move(final double x, final double y)
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size();
}
@Override
public Vect origin()
{
return t.origin().add(x, y);
}
};
}
public Rect move(final Num x, final Num y)
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size();
}
@Override
public Vect origin()
{
return t.origin().add(x, y);
}
};
}
/**
* Shrink to sides
*
* @param shrink shrink size (horizontal and vertical)
* @return result
*/
public Rect shrink(Vect shrink)
{
return shrink(shrink.x(), shrink.y());
}
/**
* Shrink to all sides
*
* @param shrink shrink
* @return result
*/
public final Rect shrink(double shrink)
{
return shrink(shrink, shrink, shrink, shrink);
}
/**
* Shrink to all sides
*
* @param shrink shrink
* @return result
*/
public final Rect shrink(Num shrink)
{
return shrink(shrink, shrink, shrink, shrink);
}
/**
* Shrink to sides at sides
*
* @param x horizontal shrink
* @param y vertical shrink
* @return result
*/
public Rect shrink(double x, double y)
{
return shrink(x, x, y, y);
}
/**
* Shrink the rect
*
* @param left shrink
* @param right shrink
* @param top shrink
* @param bottom shrink
* @return result
*/
public Rect shrink(final double left, final double right, final double top, final double bottom)
{
return grow(-left, -right, -top, -bottom);
}
public Rect shrinkLeft(final double shrink)
{
return growLeft(-shrink);
}
public Rect shrinkRight(final double shrink)
{
return growLeft(-shrink);
}
public Rect shrinkTop(final double shrink)
{
return growUp(-shrink);
}
public Rect shrinkBottom(final double shrink)
{
return growDown(-shrink);
}
public Rect growLeft(final double shrink)
{
return grow(shrink, 0, 0, 0);
}
public Rect growRight(final double shrink)
{
return grow(0, shrink, 0, 0);
}
public Rect growUp(final double shrink)
{
return grow(0, 0, shrink, 0);
}
public Rect growDown(final double shrink)
{
return grow(0, 0, 0, shrink);
}
public Rect shrinkLeft(final Num shrink)
{
return shrink(shrink, Num.ZERO, Num.ZERO, Num.ZERO);
}
public Rect shrinkRight(final Num shrink)
{
return shrink(Num.ZERO, shrink, Num.ZERO, Num.ZERO);
}
public Rect shrinkTop(final Num shrink)
{
return shrink(Num.ZERO, Num.ZERO, shrink, Num.ZERO);
}
public Rect shrinkBottom(final Num shrink)
{
return shrink(Num.ZERO, Num.ZERO, Num.ZERO, shrink);
}
public Rect growLeft(final Num shrink)
{
return grow(shrink, Num.ZERO, Num.ZERO, Num.ZERO);
}
public Rect growRight(final Num shrink)
{
return grow(Num.ZERO, shrink, Num.ZERO, Num.ZERO);
}
public Rect growUp(final Num shrink)
{
return grow(Num.ZERO, Num.ZERO, shrink, Num.ZERO);
}
public Rect growDown(final Num shrink)
{
return grow(Num.ZERO, Num.ZERO, Num.ZERO, shrink);
}
/**
* Grow to sides
*
* @param grow grow size (added to each side)
* @return grown copy
*/
public final Rect grow(Vect grow)
{
return grow(grow.x(), grow.y());
}
/**
* Grow to all sides
*
* @param grow grow
* @return result
*/
public final Rect grow(double grow)
{
return grow(grow, grow, grow, grow);
}
/**
* Grow to all sides
*
* @param grow grow
* @return result
*/
public final Rect grow(Num grow)
{
return grow(grow, grow, grow, grow);
}
/**
* Grow to sides
*
* @param x horizontal grow
* @param y vertical grow
* @return result
*/
public final Rect grow(double x, double y)
{
return grow(x, x, y, y);
}
/**
* Grow the rect
*
* @param left growth
* @param right growth
* @param top growth
* @param bottom growth
* @return result
*/
public Rect grow(final double left, final double right, final double top, final double bottom)
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size().add(left + right, top + bottom);
}
@Override
public Vect origin()
{
return t.origin().sub(left, top);
}
};
}
public Rect shrink(final Num left, final Num right, final Num top, final Num bottom)
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size().sub(left.add(right), top.add(bottom));
}
@Override
public Vect origin()
{
return t.origin().add(left, top);
}
};
}
public Rect grow(final Num left, final Num right, final Num top, final Num bottom)
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size().add(left.add(right), top.add(bottom));
}
@Override
public Vect origin()
{
return t.origin().sub(left, top);
}
};
}
/**
* Round coords
*
* @return result
*/
public Rect round()
{
return new Rect() {
private final Rect t = Rect.this;
@Override
public Vect size()
{
return t.size().round();
}
@Override
public Vect origin()
{
return t.origin().round();
}
};
}
public Num x()
{
return p_x != null ? p_x : (p_x = origin().xn());
}
public Num y()
{
return p_y != null ? p_y : (p_y = origin().yn());
}
public Num width()
{
return p_w != null ? p_w : (p_w = size().xn());
}
public Num height()
{
return p_h != null ? p_h : (p_h = size().yn());
}
public Num left()
{
return p_l != null ? p_l : (p_l = origin().xn());
}
public Num right()
{
return p_r != null ? p_r : (p_r = origin().xn().add(size().xn()));
}
public Num top()
{
return p_t != null ? p_t : (p_t = origin().yn());
}
public Num bottom()
{
return p_b != null ? p_b : (p_b = origin().yn().add(size().yn()));
}
public Vect topLeft()
{
return origin();
}
public Vect topCenter()
{
return p_tc != null ? p_tc : (p_tc = origin().add(size().xn().half(), Num.ZERO));
}
public Vect topRight()
{
return p_tr != null ? p_tr : (p_tr = origin().add(size().xn(), Num.ZERO));
}
public Vect centerLeft()
{
return p_cl != null ? p_cl : (p_cl = origin().add(Num.ZERO, size().yn().half()));
}
public Vect center()
{
return p_cc != null ? p_cc : (p_cc = origin().add(size().half()));
}
public Vect centerRight()
{
return p_cr != null ? p_cr : (p_cr = origin().add(size().xn(), size().yn().half()));
}
public Vect bottomLeft()
{
return p_bl != null ? p_bl : (p_bl = origin().add(Num.ZERO, size().yn()));
}
public Vect bottomCenter()
{
return p_bc != null ? p_bc : (p_bc = origin().add(size().xn().half(), size().yn()));
}
public Vect bottomRight()
{
return p_br != null ? p_br : (p_br = origin().add(size().xn(), size().yn()));
}
public Rect leftEdge()
{
return p_edge_l != null ? p_edge_l : (p_edge_l = topLeft().expand(Num.ZERO, Num.ZERO, Num.ZERO, height()));
}
public Rect rightEdge()
{
return p_edge_r != null ? p_edge_r : (p_edge_r = topRight().expand(Num.ZERO, Num.ZERO, Num.ZERO, height()));
}
public Rect topEdge()
{
return p_edge_t != null ? p_edge_t : (p_edge_t = topLeft().expand(Num.ZERO, width(), Num.ZERO, Num.ZERO));
}
public Rect bottomEdge()
{
return p_edge_b != null ? p_edge_b : (p_edge_b = bottomLeft().expand(Num.ZERO, width(), Num.ZERO, Num.ZERO));
}
/**
* Center to given point
*
* @param point new center
* @return centered
*/
public Rect centerTo(final Vect point)
{
return new Rect() {
Rect t = Rect.this;
@Override
public Vect size()
{
return t.size();
}
@Override
public Vect origin()
{
return point.sub(t.size().half());
}
};
}
/**
* Check if point is inside this rectangle
*
* @param point point to test
* @return is inside
*/
public boolean contains(Vect point)
{
final double x = point.x();
final double y = point.y();
final double x1 = origin().x();
final double y1 = origin().y();
final double x2 = x1 + size().x();
final double y2 = y1 + size().y();
return x >= x1 && y >= y1 && x <= x2 && y <= y2;
}
/**
* Center to given rect's center
*
* @param parent rect to center to
* @return centered
*/
public Rect centerTo(Rect parent)
{
return centerTo(parent.center());
}
/**
* Get TiledRect with given number of evenly spaced tiles. Tile indexes are
* one-based by default.
*
* @param horizontal horizontal tile count
* @param vertical vertical tile count
* @return tiled rect
*/
public TiledRect tiles(int horizontal, int vertical)
{
return new TiledRect(this, horizontal, vertical);
}
/**
* Get TiledRect with N columns and 1 row. Column indexes are one-based by
* default.
*
* @param columns number of columns
* @return tiled rect
*/
public TiledRect columns(int columns)
{
return new TiledRect(this, columns, 1);
}
/**
* Get TiledRect with N rows and 1 column. Row indexes are one-based by
* default.
*
* @param rows number of columns
* @return tiled rect
*/
public TiledRect rows(int rows)
{
return new TiledRect(this, 1, rows);
}
}
@@ -0,0 +1,440 @@
package mightypork.util.math.constraints.rect;
import mightypork.util.math.constraints.num.NumConst;
import mightypork.util.math.constraints.rect.caching.RectDigest;
import mightypork.util.math.constraints.vect.Vect;
import mightypork.util.math.constraints.vect.VectConst;
/**
* Rectangle with constant bounds, that can never change.<br>
* It's arranged so that operations with constant arguments yield constant
* results.
*
* @author MightyPork
*/
public class RectConst extends Rect {
private final VectConst pos;
private final VectConst size;
// cached with lazy loading
private NumConst v_b;
private NumConst v_r;
private VectConst v_br;
private VectConst v_tc;
private VectConst v_tr;
private VectConst v_cl;
private VectConst v_c;
private VectConst v_cr;
private VectConst v_bl;
private VectConst v_bc;
private RectConst v_round;
private RectConst v_edge_l;
private RectConst v_edge_r;
private RectConst v_edge_t;
private RectConst v_edge_b;
private RectDigest digest;
/**
* Create at given origin, with given size.
*
* @param x
* @param y
* @param width
* @param height
*/
RectConst(double x, double y, double width, double height) {
this.pos = Vect.make(x, y);
this.size = Vect.make(width, height);
}
/**
* Create at given origin, with given size.
*
* @param origin
* @param size
*/
RectConst(Vect origin, Vect size) {
this.pos = origin.freeze();
this.size = size.freeze();
}
/**
* Create at given origin, with given size.
*
* @param another other coord
*/
RectConst(Rect another) {
this.pos = another.origin().freeze();
this.size = another.size().freeze();
}
/**
* @deprecated it's useless to copy a constant
*/
@Override
@Deprecated
public RectConst freeze()
{
return this; // already constant
}
@Override
public RectDigest digest()
{
return (digest != null) ? digest : (digest = super.digest());
}
@Override
public VectConst origin()
{
return pos;
}
@Override
public VectConst size()
{
return size;
}
@Override
public RectConst move(Vect move)
{
return move(move.x(), move.y());
}
@Override
public RectConst move(double x, double y)
{
return Rect.make(pos.add(x, y), size);
}
public RectConst move(NumConst x, NumConst y)
{
return super.move(x, y).freeze();
}
@Override
public RectConst shrink(double left, double right, double top, double bottom)
{
return super.shrink(left, right, top, bottom).freeze();
}
@Override
public RectConst grow(double left, double right, double top, double bottom)
{
return super.grow(left, right, top, bottom).freeze();
}
@Override
public RectConst round()
{
return (v_round != null) ? v_round : (v_round = Rect.make(pos.round(), size.round()));
}
@Override
public NumConst x()
{
return pos.xn();
}
@Override
public NumConst y()
{
return pos.yn();
}
@Override
public NumConst width()
{
return size.xn();
}
@Override
public NumConst height()
{
return size.yn();
}
@Override
public NumConst left()
{
return pos.xn();
}
@Override
public NumConst right()
{
return (v_r != null) ? v_r : (v_r = super.right().freeze());
}
@Override
public NumConst top()
{
return pos.yn();
}
@Override
public NumConst bottom()
{
return (v_b != null) ? v_b : (v_b = super.bottom().freeze());
}
@Override
public VectConst topLeft()
{
return pos;
}
@Override
public VectConst topCenter()
{
return (v_tc != null) ? v_tc : (v_tc = super.topCenter().freeze());
}
@Override
public VectConst topRight()
{
return (v_tr != null) ? v_tr : (v_tr = super.topRight().freeze());
}
@Override
public VectConst centerLeft()
{
return (v_cl != null) ? v_cl : (v_cl = super.centerLeft().freeze());
}
@Override
public VectConst center()
{
return (v_c != null) ? v_c : (v_c = super.center().freeze());
}
@Override
public VectConst centerRight()
{
return (v_cr != null) ? v_cr : (v_cr = super.centerRight().freeze());
}
@Override
public VectConst bottomLeft()
{
return (v_bl != null) ? v_bl : (v_bl = super.bottomLeft().freeze());
}
@Override
public VectConst bottomCenter()
{
return (v_bc != null) ? v_bc : (v_bc = super.bottomCenter().freeze());
}
@Override
public VectConst bottomRight()
{
return (v_br != null) ? v_br : (v_br = super.bottomRight().freeze());
}
@Override
public RectConst leftEdge()
{
return (v_edge_l != null) ? v_edge_l : (v_edge_l = super.leftEdge().freeze());
}
@Override
public RectConst rightEdge()
{
return (v_edge_r != null) ? v_edge_r : (v_edge_r = super.rightEdge().freeze());
}
@Override
public RectConst topEdge()
{
return (v_edge_t != null) ? v_edge_t : (v_edge_t = super.topEdge().freeze());
}
@Override
public RectConst bottomEdge()
{
return (v_edge_b != null) ? v_edge_b : (v_edge_b = super.bottomEdge().freeze());
}
@Override
public Rect shrink(Vect shrink)
{
return super.shrink(shrink);
}
@Override
public RectConst shrink(double x, double y)
{
return super.shrink(x, y).freeze();
}
public RectConst shrink(NumConst left, NumConst right, NumConst top, NumConst bottom)
{
return super.shrink(left, right, top, bottom).freeze();
}
public RectConst grow(NumConst left, NumConst right, NumConst top, NumConst bottom)
{
return super.grow(left, right, top, bottom).freeze();
}
@Override
public RectConst shrinkLeft(double shrink)
{
return super.shrinkLeft(shrink).freeze();
}
@Override
public RectConst shrinkRight(double shrink)
{
return super.shrinkRight(shrink).freeze();
}
@Override
public RectConst shrinkTop(double shrink)
{
return super.shrinkTop(shrink).freeze();
}
@Override
public RectConst shrinkBottom(double shrink)
{
return super.shrinkBottom(shrink).freeze();
}
@Override
public RectConst growLeft(double shrink)
{
return super.growLeft(shrink).freeze();
}
@Override
public RectConst growRight(double shrink)
{
return super.growRight(shrink).freeze();
}
@Override
public RectConst growUp(double shrink)
{
return super.growUp(shrink).freeze();
}
@Override
public RectConst growDown(double shrink)
{
return super.growDown(shrink).freeze();
}
public RectConst shrinkLeft(NumConst shrink)
{
return super.shrinkLeft(shrink).freeze();
}
public RectConst shrinkRight(NumConst shrink)
{
return super.shrinkRight(shrink).freeze();
}
public RectConst shrinkTop(NumConst shrink)
{
return super.shrinkTop(shrink).freeze();
}
public RectConst shrinkBottom(NumConst shrink)
{
return super.shrinkBottom(shrink).freeze();
}
public RectConst growLeft(NumConst shrink)
{
return super.growLeft(shrink).freeze();
}
public RectConst growRight(NumConst shrink)
{
return super.growRight(shrink).freeze();
}
public RectConst growUp(NumConst shrink)
{
return super.growUp(shrink).freeze();
}
public RectConst growBottom(NumConst shrink)
{
return super.growDown(shrink).freeze();
}
public RectConst centerTo(VectConst point)
{
return super.centerTo(point).freeze();
}
public RectConst centerTo(RectConst parent)
{
return super.centerTo(parent).freeze();
}
}
@@ -0,0 +1,149 @@
package mightypork.util.math.constraints.rect.builders;
import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.rect.Rect;
import mightypork.util.math.constraints.rect.proxy.RectProxy;
/**
* Utility for cutting rect into evenly sized cells.<br>
* It's by default one-based, but this can be switched by calling the oneBased()
* and zeroBased() methods.
*
* @author MightyPork
*/
public class TiledRect extends RectProxy {
final private int tilesY;
final private int tilesX;
final private Num perRow;
final private Num perCol;
private int based = 1;
public TiledRect(Rect source, int horizontal, int vertical) {
super(source);
this.tilesX = horizontal;
this.tilesY = vertical;
this.perRow = height().div(vertical);
this.perCol = width().div(horizontal);
}
/**
* Set to one-based mode, and return itself (for chaining).
*
* @return this
*/
public TiledRect oneBased()
{
based = 1;
return this;
}
/**
* Set to zero-based mode, and return itself (for chaining).
*
* @return this
*/
public TiledRect zeroBased()
{
based = 0;
return this;
}
/**
* Get a tile.
*
* @param x x position
* @param y y position
* @return tile
* @throws IndexOutOfBoundsException when invalid index is specified.
*/
public Rect tile(int x, int y)
{
x -= based;
y -= based;
if (x >= tilesX || x < 0) {
throw new IndexOutOfBoundsException("X coordinate out fo range.");
}
if (y >= tilesY || y < 0) {
throw new IndexOutOfBoundsException("Y coordinate out of range.");
}
final Num leftMove = left().add(perCol.mul(x));
final Num topMove = top().add(perRow.mul(y));
return Rect.make(perCol, perRow).move(leftMove, topMove);
}
/**
* Get a span (tile spanning across multiple cells)
*
* @param x_from x start position
* @param y_from y start position
* @param size_x horizontal size (columns)
* @param size_y vertical size (rows)
* @return tile the tile
* @throws IndexOutOfBoundsException when invalid index is specified.
*/
public Rect span(int x_from, int y_from, int size_x, int size_y)
{
x_from -= based;
y_from -= based;
final int x_to = x_from + size_x;
final int y_to = y_from + size_y;
if (size_x <= 0 || size_y <= 0) {
throw new IndexOutOfBoundsException("Size must be > 0.");
}
if (x_from >= tilesX || x_from < 0 || x_to >= tilesX || x_to < 0) {
throw new IndexOutOfBoundsException("X coordinate(s) out of range.");
}
if (y_from >= tilesY || y_from < 0 || y_to >= tilesY || y_to < 0) {
throw new IndexOutOfBoundsException("Y coordinate(s) out of range.");
}
final Num leftMove = left().add(perCol.mul(x_from));
final Num topMove = top().add(perRow.mul(y_from));
return Rect.make(perCol.mul(size_x), perRow.mul(size_y)).move(leftMove, topMove);
}
/**
* Get n-th column
*
* @param n column index
* @return the column tile
* @throws IndexOutOfBoundsException when invalid index is specified.
*/
public Rect column(int n)
{
return tile(n, based);
}
/**
* Get n-th row
*
* @param n row index
* @return the row rect
* @throws IndexOutOfBoundsException when invalid index is specified.
*/
public Rect row(int n)
{
return tile(based, n);
}
}
@@ -0,0 +1,74 @@
package mightypork.util.math.constraints.rect.caching;
import mightypork.util.math.constraints.ConstraintCache;
import mightypork.util.math.constraints.rect.Rect;
import mightypork.util.math.constraints.rect.mutable.RectVar;
import mightypork.util.math.constraints.rect.proxy.RectAdapter;
/**
* <p>
* A rect cache.
* </p>
* <p>
* Values are held in a caching VectVar, and digest caching is enabled by
* default.
* </p>
*
* @author MightyPork
*/
public abstract class AbstractRectCache extends RectAdapter implements ConstraintCache<Rect> {
private final RectVar cache = Rect.makeVar();
private boolean inited = false;
private boolean cachingEnabled = true;
public AbstractRectCache() {
enableDigestCaching(true); // it changes only on poll
}
@Override
protected final Rect getSource()
{
if (!inited) poll();
return (cachingEnabled ? cache : getCacheSource());
}
@Override
public final void poll()
{
inited = true;
// poll source
final Rect source = getCacheSource();
source.markDigestDirty(); // poll cached
// store source value
cache.setTo(source);
markDigestDirty();
onChange();
}
@Override
public final void enableCaching(boolean yes)
{
cachingEnabled = yes;
enableDigestCaching(yes);
}
@Override
public final boolean isCachingEnabled()
{
return cachingEnabled;
}
}
@@ -0,0 +1,36 @@
package mightypork.util.math.constraints.rect.caching;
import mightypork.util.annotations.DefaultImpl;
import mightypork.util.math.constraints.rect.Rect;
/**
* Rect cache implementation
*
* @author MightyPork
*/
public class RectCache extends AbstractRectCache {
private final Rect source;
public RectCache(Rect source) {
this.source = source;
}
@Override
public final Rect getCacheSource()
{
return source;
}
@Override
@DefaultImpl
public void onChange()
{
}
}
@@ -0,0 +1,43 @@
package mightypork.util.math.constraints.rect.caching;
import mightypork.util.math.constraints.rect.Rect;
import mightypork.util.math.constraints.rect.RectConst;
public class RectDigest {
public final double x;
public final double y;
public final double width;
public final double height;
public final double left;
public final double right;
public final double top;
public final double bottom;
public RectDigest(Rect rect) {
final RectConst frozen = rect.freeze();
this.x = frozen.x().value();
this.y = frozen.y().value();
this.width = frozen.width().value();
this.height = frozen.height().value();
this.left = frozen.left().value();
this.right = frozen.right().value();
this.top = frozen.top().value();
this.bottom = frozen.bottom().value();
}
@Override
public String toString()
{
return String.format("Rect{ at: (%.1f, %.1f), size: (%.1f, %.1f), bounds: L %.1f R %.1f T %.1f B %.1f }", x, y, width, height, left, right, top, bottom);
}
}
@@ -0,0 +1,90 @@
package mightypork.util.math.constraints.rect.mutable;
import mightypork.util.math.constraints.rect.Rect;
import mightypork.util.math.constraints.vect.Vect;
/**
* Mutable rectangle; operations change it's state.
*
* @author MightyPork
*/
public abstract class RectMutable extends Rect {
/**
* Set to other rect's coordinates
*
* @param rect other rect
*/
public void setTo(Rect rect)
{
setTo(rect.origin(), rect.size());
}
/**
* Set to given size and position
*
* @param origin new origin
* @param width new width
* @param height new height
*/
public void setTo(Vect origin, double width, double height)
{
setTo(origin, Vect.make(width, height));
}
/**
* Set to given size and position
*
* @param x origin.x
* @param y origin.y
* @param width new width
* @param height new height
*/
public void setTo(double x, double y, double width, double height)
{
setTo(Vect.make(x, y), Vect.make(width, height));
}
/**
* Set to given size and position
*
* @param origin new origin
* @param size new size
*/
public void setTo(Vect origin, Vect size)
{
setOrigin(origin);
setSize(size);
}
/**
* Set to zero
*/
public void reset()
{
setTo(Vect.ZERO, Vect.ZERO);
}
/**
* Set new origin
*
* @param origin new origin
*/
public abstract void setOrigin(Vect origin);
/**
* Set new size
*
* @param size new size
*/
public abstract void setSize(Vect size);
}
@@ -0,0 +1,54 @@
package mightypork.util.math.constraints.rect.mutable;
import mightypork.util.math.constraints.vect.Vect;
import mightypork.util.math.constraints.vect.mutable.VectVar;
public class RectVar extends RectMutable {
final VectVar pos = Vect.makeVar();
final VectVar size = Vect.makeVar();
/**
* Create at given origin, with given size.
*
* @param x
* @param y
* @param width
* @param height
*/
public RectVar(double x, double y, double width, double height) {
this.pos.setTo(x, y);
this.size.setTo(width, height);
}
@Override
public Vect origin()
{
return pos;
}
@Override
public Vect size()
{
return size;
}
@Override
public void setOrigin(Vect origin)
{
this.pos.setTo(origin);
}
@Override
public void setSize(Vect size)
{
this.size.setTo(size);
}
}
@@ -0,0 +1,16 @@
package mightypork.util.math.constraints.rect.proxy;
/**
* Pluggable rect bound
*
* @author MightyPork
*/
public interface PluggableRectBound extends RectBound {
/**
* @param rect context to set
*/
abstract void setRect(RectBound rect);
}
@@ -0,0 +1,58 @@
package mightypork.util.math.constraints.rect.proxy;
import mightypork.util.math.constraints.rect.Rect;
import mightypork.util.math.constraints.vect.Vect;
import mightypork.util.math.constraints.vect.proxy.VectAdapter;
/**
* Rect proxy with abstract method for plugging in / generating rect
* dynamically.
*
* @author MightyPork
*/
public abstract class RectAdapter extends Rect {
// adapters are needed in case the vect returned from source changes
// (is replaced). This way, references to origin and rect will stay intact.
private final VectAdapter originAdapter = new VectAdapter() {
@Override
protected Vect getSource()
{
return RectAdapter.this.getSource().origin();
}
};
private final VectAdapter sizeAdapter = new VectAdapter() {
@Override
protected Vect getSource()
{
return RectAdapter.this.getSource().size();
}
};
/**
* @return the proxied coord
*/
protected abstract Rect getSource();
@Override
public Vect origin()
{
return originAdapter;
}
@Override
public Vect size()
{
return sizeAdapter;
}
}
@@ -0,0 +1,18 @@
package mightypork.util.math.constraints.rect.proxy;
import mightypork.util.math.constraints.rect.Rect;
/**
* Rect constraint (ie. region)
*
* @author MightyPork
*/
public interface RectBound {
/**
* @return rect region
*/
Rect getRect();
}
@@ -0,0 +1,39 @@
package mightypork.util.math.constraints.rect.proxy;
import mightypork.util.math.constraints.rect.Rect;
/**
* Pluggable rect bound adapter
*
* @author MightyPork
*/
public class RectBoundAdapter extends RectAdapter implements PluggableRectBound {
private RectBound backing = null;
public RectBoundAdapter() {
}
public RectBoundAdapter(RectBound bound) {
backing = bound;
}
@Override
public void setRect(RectBound rect)
{
this.backing = rect;
}
@Override
public Rect getSource()
{
return backing.getRect();
}
}
@@ -0,0 +1,23 @@
package mightypork.util.math.constraints.rect.proxy;
import mightypork.util.math.constraints.rect.Rect;
public class RectProxy extends RectAdapter {
private final Rect source;
public RectProxy(Rect source) {
this.source = source;
}
@Override
protected Rect getSource()
{
return source;
}
}
@@ -0,0 +1,38 @@
package mightypork.util.math.constraints.rect.proxy;
import mightypork.util.math.constraints.rect.Rect;
import mightypork.util.math.constraints.vect.Vect;
/**
* Rect made of two {@link Vect}s
*
* @author MightyPork
*/
public class RectVectAdapter extends Rect {
private final Vect origin;
private final Vect size;
public RectVectAdapter(Vect origin, Vect size) {
this.origin = origin;
this.size = size;
}
@Override
public Vect origin()
{
return origin;
}
@Override
public Vect size()
{
return size;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,373 @@
package mightypork.util.math.constraints.vect;
import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.num.NumConst;
import mightypork.util.math.constraints.rect.RectConst;
import mightypork.util.math.constraints.vect.caching.VectDigest;
/**
* Coordinate with immutable numeric values.<br>
* This coordinate is guaranteed to never change, as opposed to view.<br>
* It's arranged so that operations with constant arguments yield constant
* results.
*
* @author MightyPork
*/
public final class VectConst extends Vect {
private final double x, y, z;
// non-parametric operations are cached using lazy load.
private NumConst v_size;
private VectConst v_neg;
private VectConst v_ceil;
private VectConst v_floor;
private VectConst v_round;
private VectConst v_half;
private VectConst v_abs;
private NumConst v_xc;
private NumConst v_yc;
private NumConst v_zc;
private VectDigest digest;
VectConst(Vect other) {
this(other.x(), other.y(), other.z());
}
VectConst(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public double x()
{
return x;
}
@Override
public double y()
{
return y;
}
@Override
public double z()
{
return z;
}
/**
* @return X constraint
*/
@Override
public final NumConst xn()
{
return (v_xc != null) ? v_xc : (v_xc = Num.make(this.x));
}
/**
* @return Y constraint
*/
@Override
public final NumConst yn()
{
return (v_yc != null) ? v_yc : (v_yc = Num.make(this.y));
}
/**
* @return Z constraint
*/
@Override
public final NumConst zn()
{
return (v_zc != null) ? v_zc : (v_zc = Num.make(this.z));
}
/**
* @deprecated it's useless to copy a constant
*/
@Override
@Deprecated
public VectConst freeze()
{
return this; // it's constant already
}
@Override
public VectDigest digest()
{
return (digest != null) ? digest : (digest = super.digest());
}
@Override
public VectConst abs()
{
return (v_abs != null) ? v_abs : (v_abs = super.abs().freeze());
}
@Override
public VectConst add(double x, double y)
{
return super.add(x, y).freeze();
}
@Override
public VectConst add(double x, double y, double z)
{
return super.add(x, y, z).freeze();
}
@Override
public VectConst half()
{
return (v_half != null) ? v_half : (v_half = super.half().freeze());
}
@Override
public VectConst mul(double d)
{
return super.mul(d).freeze();
}
@Override
public VectConst mul(double x, double y)
{
return super.mul(x, y).freeze();
}
@Override
public VectConst mul(double x, double y, double z)
{
return super.mul(x, y, z).freeze();
}
@Override
public VectConst round()
{
return (v_round != null) ? v_round : (v_round = super.round().freeze());
}
@Override
public VectConst floor()
{
return (v_floor != null) ? v_floor : (v_floor = super.floor().freeze());
}
@Override
public VectConst ceil()
{
if (v_ceil != null) return v_ceil;
return v_ceil = super.ceil().freeze();
}
@Override
public VectConst sub(double x, double y)
{
return super.sub(x, y).freeze();
}
@Override
public VectConst sub(double x, double y, double z)
{
return super.sub(x, y, z).freeze();
}
@Override
public VectConst neg()
{
return (v_neg != null) ? v_neg : (v_neg = super.neg().freeze());
}
@Override
public VectConst norm(double size)
{
return super.norm(size).freeze();
}
@Override
public NumConst size()
{
return (v_size != null) ? v_size : (v_size = super.size().freeze());
}
@Override
public VectConst withX(double x)
{
return super.withX(x).freeze();
}
@Override
public VectConst withY(double y)
{
return super.withY(y).freeze();
}
@Override
public VectConst withZ(double z)
{
return super.withZ(z).freeze();
}
public VectConst withX(NumConst x)
{
return super.withX(x).freeze();
}
public VectConst withY(NumConst y)
{
return super.withY(y).freeze();
}
public VectConst withZ(NumConst z)
{
return super.withZ(z).freeze();
}
public VectConst add(VectConst vec)
{
return super.add(vec).freeze();
}
public VectConst add(NumConst x, NumConst y)
{
return super.add(x, y).freeze();
}
public VectConst add(NumConst x, NumConst y, NumConst z)
{
return super.add(x, y, z).freeze();
}
public VectConst mul(VectConst vec)
{
return super.mul(vec).freeze();
}
public VectConst mul(NumConst d)
{
return super.mul(d).freeze();
}
public VectConst mul(NumConst x, NumConst y)
{
return super.mul(x, y).freeze();
}
public VectConst mul(NumConst x, NumConst y, NumConst z)
{
return super.mul(x, y, z).freeze();
}
public VectConst sub(VectConst vec)
{
return super.sub(vec).freeze();
}
public VectConst sub(NumConst x, NumConst y)
{
return super.sub(x, y).freeze();
}
public VectConst sub(NumConst x, NumConst y, NumConst z)
{
return super.sub(x, y, z).freeze();
}
public VectConst norm(NumConst size)
{
return super.norm(size).freeze();
}
public NumConst dist(VectConst point)
{
return super.dist(point).freeze();
}
public VectConst midTo(VectConst point)
{
return super.midTo(point).freeze();
}
public VectConst vectTo(VectConst point)
{
return super.vectTo(point).freeze();
}
public NumConst dot(VectConst vec)
{
return super.dot(vec).freeze();
}
public VectConst cross(VectConst vec)
{
return super.cross(vec).freeze();
}
@Override
public RectConst expand(double left, double right, double top, double bottom)
{
return super.expand(left, right, top, bottom).freeze();
}
public RectConst expand(NumConst left, NumConst right, NumConst top, NumConst bottom)
{
return super.expand(left, right, top, bottom).freeze();
}
}
@@ -0,0 +1,23 @@
package mightypork.util.math.constraints.vect;
import mightypork.util.math.constraints.vect.proxy.VectAdapter;
public class VectProxy extends VectAdapter {
private final Vect source;
public VectProxy(Vect source) {
this.source = source;
}
@Override
protected Vect getSource()
{
return source;
}
}
@@ -0,0 +1,74 @@
package mightypork.util.math.constraints.vect.caching;
import mightypork.util.math.constraints.ConstraintCache;
import mightypork.util.math.constraints.vect.Vect;
import mightypork.util.math.constraints.vect.mutable.VectVar;
import mightypork.util.math.constraints.vect.proxy.VectAdapter;
/**
* <p>
* A vect cache.
* </p>
* <p>
* Values are held in a caching VectVar, and digest caching is enabled by
* default.
* </p>
*
* @author MightyPork
*/
public abstract class AbstractVectCache extends VectAdapter implements ConstraintCache<Vect> {
private final VectVar cache = Vect.makeVar();
private boolean inited = false;
private boolean cachingEnabled = true;
public AbstractVectCache() {
enableDigestCaching(true); // it changes only on poll
}
@Override
protected final Vect getSource()
{
if (!inited) markDigestDirty();
return (cachingEnabled ? cache : getCacheSource());
}
@Override
public final void poll()
{
inited = true;
// poll source
final Vect source = getCacheSource();
source.markDigestDirty(); // poll cached
// store source value
cache.setTo(source);
markDigestDirty();
onChange();
}
@Override
public final void enableCaching(boolean yes)
{
cachingEnabled = yes;
enableDigestCaching(yes);
}
@Override
public final boolean isCachingEnabled()
{
return cachingEnabled;
}
}
@@ -0,0 +1,36 @@
package mightypork.util.math.constraints.vect.caching;
import mightypork.util.annotations.DefaultImpl;
import mightypork.util.math.constraints.vect.Vect;
/**
* Vect cache implementation
*
* @author MightyPork
*/
public class VectCache extends AbstractVectCache {
private final Vect source;
public VectCache(Vect source) {
this.source = source;
enableDigestCaching(true);
}
@Override
public Vect getCacheSource()
{
return source;
}
@Override
@DefaultImpl
public void onChange()
{
}
}
@@ -0,0 +1,26 @@
package mightypork.util.math.constraints.vect.caching;
import mightypork.util.math.constraints.vect.Vect;
public class VectDigest {
public final double x;
public final double y;
public final double z;
public VectDigest(Vect vect) {
this.x = vect.x();
this.y = vect.y();
this.z = vect.z();
}
@Override
public String toString()
{
return String.format("Vect(%.1f, %.1f, %.1f)", x, y, z);
}
}
@@ -0,0 +1,291 @@
package mightypork.util.math.constraints.vect.mutable;
import mightypork.util.annotations.FactoryMethod;
import mightypork.util.control.timing.Pauseable;
import mightypork.util.control.timing.Updateable;
import mightypork.util.math.Easing;
import mightypork.util.math.constraints.num.mutable.NumAnimated;
import mightypork.util.math.constraints.vect.Vect;
/**
* 3D coordinated with support for transitions, mutable.
*
* @author MightyPork
*/
public class VectAnimated extends VectMutable implements Pauseable, Updateable {
private final NumAnimated x, y, z;
private double defaultDuration = 0;
/**
* Create an animated vector; This way different easing / settings can be
* specified for each coordinate.
*
* @param x x animator
* @param y y animator
* @param z z animator
*/
public VectAnimated(NumAnimated x, NumAnimated y, NumAnimated z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Create an animated vector
*
* @param start initial positioon
* @param easing animation easing
*/
public VectAnimated(Vect start, Easing easing) {
x = new NumAnimated(start.x(), easing);
y = new NumAnimated(start.y(), easing);
z = new NumAnimated(start.z(), easing);
}
@Override
public double x()
{
return x.value();
}
@Override
public double y()
{
return y.value();
}
@Override
public double z()
{
return z.value();
}
@Override
public void setTo(double x, double y, double z)
{
setX(x);
setY(y);
setZ(z);
}
@Override
public void setX(double x)
{
this.x.animate(x, defaultDuration);
}
@Override
public void setY(double y)
{
this.y.animate(y, defaultDuration);
}
@Override
public void setZ(double z)
{
this.z.animate(z, defaultDuration);
}
/**
* Add offset with animation
*
* @param offset added offset
* @param duration animation time (seconds)
*/
public void add(Vect offset, double duration)
{
animate(this.add(offset), duration);
}
/**
* Animate to given coordinates in given amount of time
*
* @param x
* @param y
* @param z
* @param duration animation time (seconds)
* @return this
*/
public VectAnimated animate(double x, double y, double z, double duration)
{
this.x.animate(x, duration);
this.y.animate(y, duration);
this.z.animate(z, duration);
return this;
}
/**
* Animate to given vec in given amount of time.
*
* @param target target (only it's current value will be used)
* @param duration animation time (seconds)
* @return this
*/
public VectAnimated animate(Vect target, double duration)
{
animate(target.x(), target.y(), target.z(), duration);
return this;
}
/**
* @return the default duration (seconds)
*/
public double getDefaultDuration()
{
return defaultDuration;
}
/**
* Set default animation duration (when changed without using animate())
*
* @param defaultDuration default duration (seconds)
*/
public void setDefaultDuration(double defaultDuration)
{
this.defaultDuration = defaultDuration;
}
@Override
public void update(double delta)
{
x.update(delta);
y.update(delta);
z.update(delta);
}
@Override
public void pause()
{
x.pause();
y.pause();
z.pause();
}
@Override
public void resume()
{
x.resume();
y.resume();
z.resume();
}
@Override
public boolean isPaused()
{
return x.isPaused(); // BÚNO
}
/**
* @return true if the animation is finished
*/
public boolean isFinished()
{
return x.isFinished(); // BÚNO
}
/**
* @return current animation duration
*/
public double getDuration()
{
return x.getDuration(); // BÚNO
}
/**
* @return elapsed time since the start of the animation
*/
public double getElapsed()
{
return x.getElapsed(); // BÚNO
}
/**
* @return animation progress (elapsed / duration)
*/
public double getProgress()
{
return x.getProgress(); // BÚNO
}
/**
* Set easing for all three coordinates
*
* @param easing
*/
public void setEasing(Easing easing)
{
x.setEasing(easing);
y.setEasing(easing);
z.setEasing(easing);
}
/**
* Create an animated vector; This way different easing / settings can be
* specified for each coordinate.
*
* @param x x animator
* @param y y animator
* @param z z animator
* @return animated mutable vector
*/
@FactoryMethod
public static VectAnimated makeVar(NumAnimated x, NumAnimated y, NumAnimated z)
{
return new VectAnimated(x, y, z);
}
/**
* Create an animated vector
*
* @param start initial positioon
* @param easing animation easing
* @return animated mutable vector
*/
@FactoryMethod
public static VectAnimated makeVar(Vect start, Easing easing)
{
return new VectAnimated(start, easing);
}
/**
* Create an animated vector, initialized at 0,0,0
*
* @param easing animation easing
* @return animated mutable vector
*/
@FactoryMethod
public static VectAnimated makeVar(Easing easing)
{
return new VectAnimated(Vect.ZERO, easing);
}
}
@@ -0,0 +1,80 @@
package mightypork.util.math.constraints.vect.mutable;
import mightypork.util.math.constraints.vect.Vect;
/**
* Mutable coord
*
* @author MightyPork
*/
abstract class VectMutable extends Vect {
/**
* Set all to zeros.
*/
public void reset()
{
setTo(0, 0, 0);
}
/**
* Set coordinates to match other coord.
*
* @param copied coord whose coordinates are used
*/
public void setTo(Vect copied)
{
setTo(copied.x(), copied.y(), copied.z());
}
/**
* Set 2D coordinates.<br>
* Z is unchanged.
*
* @param x x coordinate
* @param y y coordinate
*/
public void setTo(double x, double y)
{
setX(x);
setY(y);
}
/**
* Set coordinates.
*
* @param x x coordinate
* @param y y coordinate
* @param z z coordinate
*/
public abstract void setTo(double x, double y, double z);
/**
* Set X coordinate.
*
* @param x x coordinate
*/
public abstract void setX(double x);
/**
* Set Y coordinate.
*
* @param y y coordinate
*/
public abstract void setY(double y);
/**
* Set Z coordinate.
*
* @param z z coordinate
*/
public abstract void setZ(double z);
}
@@ -0,0 +1,77 @@
package mightypork.util.math.constraints.vect.mutable;
/**
* Mutable coordinate.<br>
* All Vec methods (except copy) alter data values and return this instance.
*
* @author MightyPork
*/
public class VectVar extends VectMutable {
private double x, y, z;
/**
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
*/
public VectVar(double x, double y, double z) {
super();
this.x = x;
this.y = y;
this.z = z;
}
@Override
public double x()
{
return x;
}
@Override
public double y()
{
return y;
}
@Override
public double z()
{
return z;
}
@Override
public void setTo(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
@Override
public void setX(double x)
{
this.x = x;
}
@Override
public void setY(double y)
{
this.y = y;
}
@Override
public void setZ(double z)
{
this.z = z;
}
}
@@ -0,0 +1,16 @@
package mightypork.util.math.constraints.vect.proxy;
/**
* Pluggable vector constraint
*
* @author MightyPork
*/
public interface PluggableVectBound extends VectBound {
/**
* @param num bound to set
*/
abstract void setVect(VectBound num);
}
@@ -0,0 +1,41 @@
package mightypork.util.math.constraints.vect.proxy;
import mightypork.util.math.constraints.vect.Vect;
/**
* Vect proxy with abstract method for plugging in / generating coordinates
* dynamically.
*
* @author MightyPork
*/
public abstract class VectAdapter extends Vect {
/**
* @return the proxied coord
*/
protected abstract Vect getSource();
@Override
public double x()
{
return getSource().x();
}
@Override
public double y()
{
return getSource().y();
}
@Override
public double z()
{
return getSource().z();
}
}
@@ -0,0 +1,18 @@
package mightypork.util.math.constraints.vect.proxy;
import mightypork.util.math.constraints.vect.Vect;
/**
* Element holding a vector, used for constraint building.
*
* @author MightyPork
*/
public interface VectBound {
/**
* @return the current vector.
*/
public Vect getVect();
}
@@ -0,0 +1,34 @@
package mightypork.util.math.constraints.vect.proxy;
import mightypork.util.math.constraints.vect.Vect;
public class VectBoundAdapter extends VectAdapter implements PluggableVectBound {
private VectBound backing = null;
public VectBoundAdapter() {
}
public VectBoundAdapter(VectBound bound) {
backing = bound;
}
@Override
public void setVect(VectBound rect)
{
this.backing = rect;
}
@Override
protected Vect getSource()
{
return backing.getVect();
}
}
@@ -0,0 +1,55 @@
package mightypork.util.math.constraints.vect.proxy;
import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.num.proxy.NumBound;
import mightypork.util.math.constraints.vect.Vect;
/**
* Coord view composed of given {@link NumBound}s, using their current values.
*
* @author MightyPork
*/
public class VectNumAdapter extends Vect {
private final Num constrX;
private final Num constrY;
private final Num constrZ;
public VectNumAdapter(Num x, Num y, Num z) {
this.constrX = x;
this.constrY = y;
this.constrZ = z;
}
public VectNumAdapter(Num x, Num y) {
this.constrX = x;
this.constrY = y;
this.constrZ = Num.ZERO;
}
@Override
public double x()
{
return constrX.value();
}
@Override
public double y()
{
return constrY.value();
}
@Override
public double z()
{
return constrZ.value();
}
}