mirror of
https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02.git
synced 2025-07-31 09:07:40 +02:00
added contents
This commit is contained in:
5
Projekte/common/build.gradle
Normal file
5
Projekte/common/build.gradle
Normal file
@@ -0,0 +1,5 @@
|
||||
plugins {
|
||||
id 'buildlogic.java-library-conventions'
|
||||
}
|
||||
|
||||
description = 'Common classes'
|
7
Projekte/common/logging.properties
Normal file
7
Projekte/common/logging.properties
Normal file
@@ -0,0 +1,7 @@
|
||||
handlers=java.util.logging.ConsoleHandler
|
||||
.level=INFO
|
||||
;pp.level=FINE
|
||||
pp.util.triangulation.Polygon.level=FINE
|
||||
java.util.logging.ConsoleHandler.level=FINER
|
||||
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
||||
java.util.logging.SimpleFormatter.format=[%4$s %2$s] %5$s%6$s%n
|
257
Projekte/common/src/main/java/pp/util/Angle.java
Normal file
257
Projekte/common/src/main/java/pp/util/Angle.java
Normal file
@@ -0,0 +1,257 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static pp.util.FloatMath.DEG_TO_RAD;
|
||||
import static pp.util.FloatMath.FLT_EPSILON;
|
||||
import static pp.util.FloatMath.RAD_TO_DEG;
|
||||
import static pp.util.FloatMath.abs;
|
||||
import static pp.util.FloatMath.atan2;
|
||||
import static pp.util.FloatMath.cos;
|
||||
import static pp.util.FloatMath.sin;
|
||||
import static pp.util.FloatMath.sqrt;
|
||||
|
||||
/**
|
||||
* A class for representing angles by unit vectors where angles define
|
||||
* polar coordinates.
|
||||
*/
|
||||
public class Angle implements Comparable<Angle> {
|
||||
/**
|
||||
* 0 degrees, i.e., along the x-axis
|
||||
*/
|
||||
public static final Angle ZERO = new Angle(1f, 0f);
|
||||
/**
|
||||
* 180 degrees, i.e., along the negative x-axis
|
||||
*/
|
||||
public static final Angle PI = new Angle(-1f, 0f);
|
||||
|
||||
/**
|
||||
* Returns the minimum of the specified angles with respect to
|
||||
* {@linkplain #compareTo(Angle)}.
|
||||
*
|
||||
* @param u first angle to compare
|
||||
* @param v second angle to compare
|
||||
*/
|
||||
public static Angle min(Angle u, Angle v) {
|
||||
return u.compareTo(v) <= 0 ? u : v;
|
||||
}
|
||||
|
||||
/**
|
||||
* The x-coordinate of the unit vector.
|
||||
*/
|
||||
public final float x;
|
||||
/**
|
||||
* The y-coordinate of the unit vector.
|
||||
*/
|
||||
public final float y;
|
||||
|
||||
/**
|
||||
* Creates a new angle represented by the vector with the specified
|
||||
* coordinates. Note that the vector must have length 1.
|
||||
*/
|
||||
private Angle(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the polar coordinates angle of the specified vector.
|
||||
*
|
||||
* @param x the vectors x value
|
||||
* @param y the vectors y value
|
||||
*/
|
||||
public static Angle fromVector(float x, float y) {
|
||||
final float len = sqrt(x * x + y * y);
|
||||
if (len < FLT_EPSILON)
|
||||
throw new IllegalArgumentException("null vector");
|
||||
return new Angle(x / len, y / len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the polar coordinates angle when looking from the first to the second
|
||||
* specified position.
|
||||
*
|
||||
* @param from first position
|
||||
* @param to second position
|
||||
*/
|
||||
public static Angle direction(Position from, Position to) {
|
||||
return fromVector(to.getX() - from.getX(),
|
||||
to.getY() - from.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Angle object for the specified angle in radians.
|
||||
*
|
||||
* @param radians the specified radian value
|
||||
*/
|
||||
public static Angle fromRadians(float radians) {
|
||||
return new Angle(cos(radians), sin(radians));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Angle object for the specified angle in degrees.
|
||||
*
|
||||
* @param degrees the specified degrees value
|
||||
*/
|
||||
public static Angle fromDegrees(float degrees) {
|
||||
return fromRadians(degrees * DEG_TO_RAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this angle in radians in the range (-pi,pi].
|
||||
*/
|
||||
public float radians() {
|
||||
return atan2(y, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this angle in degrees in the range (-180,180].
|
||||
*/
|
||||
public float degrees() {
|
||||
return radians() * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x-coordinate of the unit vector, that is the cosine of the angle.
|
||||
*/
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate of the unit vector, that is the sinus of the angle.
|
||||
*/
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the angle obtained by adding the specified angle to this angle.
|
||||
*
|
||||
* @param o the other angle
|
||||
*/
|
||||
public Angle plus(Angle o) {
|
||||
return new Angle(x * o.x - y * o.y,
|
||||
x * o.y + y * o.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the angle obtained by subtracting the specified angle from this angle.
|
||||
*
|
||||
* @param o the other angle
|
||||
*/
|
||||
public Angle minus(Angle o) {
|
||||
return new Angle(y * o.y + x * o.x,
|
||||
y * o.x - x * o.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bisector angle between this angle as left angle and the
|
||||
* specified right angle, that is, the angle halfway from the right to this
|
||||
* angle when turning from right to left.
|
||||
*
|
||||
* @param right right angle
|
||||
* @return the bisector angle between this as left and the specified right angle
|
||||
*/
|
||||
public Angle bisector(Angle right) {
|
||||
// compute vector of this.minus(right)
|
||||
final float dx = y * right.y + x * right.x;
|
||||
final float dy = y * right.x - x * right.y;
|
||||
if (abs(dy) < FLT_EPSILON) {
|
||||
// the difference is either 0° or 180°
|
||||
if (dx > 0f)
|
||||
return this;
|
||||
else
|
||||
return new Angle(-right.y, right.x);
|
||||
}
|
||||
final float mid = 0.5f * atan2(dy, dx);
|
||||
final float sum = right.radians() + mid;
|
||||
if (mid > 0f)
|
||||
return new Angle(cos(sum), sin(sum));
|
||||
else
|
||||
return new Angle(-cos(sum), -sin(sum));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if turning left from the specified angle towards this angle is
|
||||
* shorter than turning right towards this angle.
|
||||
*
|
||||
* @param o another angle
|
||||
*/
|
||||
public boolean leftOf(Angle o) {
|
||||
return y * o.x - x * o.y > 0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if turning right from the specified angle towards this angle is
|
||||
* shorter than turning left towards this angle.
|
||||
*
|
||||
* @param o another angle
|
||||
*/
|
||||
public boolean rightOf(Angle o) {
|
||||
return y * o.x - x * o.y < 0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this angle with the specified one and returns -1, 0, or 1 if
|
||||
* the value of this angle is less than, equal to, or greater than the value
|
||||
* of the specified angle, respectively, where angle values are in the
|
||||
* range [0,2*pi) and 0 means along the x-axis.
|
||||
*
|
||||
* @param o the other angle
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Angle o) {
|
||||
if (y == 0f) {
|
||||
if (o.y < 0f)
|
||||
return -1;
|
||||
if (o.y > 0f)
|
||||
return x > 0f ? -1 : 1;
|
||||
if (x > 0f)
|
||||
return o.x > 0f ? 0 : 1;
|
||||
return o.x > 0f ? -1 : 0;
|
||||
}
|
||||
if (y > 0f) {
|
||||
if (o.y < 0f)
|
||||
return -1;
|
||||
if (o.y == 0f)
|
||||
return o.x > 0f ? 1 : -1;
|
||||
}
|
||||
else if (o.y >= 0f)
|
||||
return 1;
|
||||
final float det = x * o.y - y * o.x;
|
||||
if (det == 0f)
|
||||
return 0;
|
||||
return det > 0f ? -1 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o instanceof Angle angle)
|
||||
return compareTo(angle) == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this angle in degrees in the range [0,2*pi).
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (degrees() < 0f)
|
||||
return (degrees() + 360f) + "°";
|
||||
return degrees() + "°";
|
||||
}
|
||||
}
|
425
Projekte/common/src/main/java/pp/util/FloatMath.java
Normal file
425
Projekte/common/src/main/java/pp/util/FloatMath.java
Normal file
@@ -0,0 +1,425 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
/**
|
||||
* Provides mathematical functions using float precision.
|
||||
*/
|
||||
public class FloatMath {
|
||||
private FloatMath() { /* don't instantiate */ }
|
||||
|
||||
public static final double DBL_EPSILON = 2.220446049250313E-16d;
|
||||
/**
|
||||
* A "close to zero" float epsilon value for use
|
||||
*/
|
||||
public static final float FLT_EPSILON = 1.1920928955078125E-7f;
|
||||
/**
|
||||
* A "close to zero" float epsilon value for use
|
||||
*/
|
||||
public static final float ZERO_TOLERANCE = 0.0001f;
|
||||
/**
|
||||
* The value 1/3, as a float.
|
||||
*/
|
||||
public static final float ONE_THIRD = 1f / 3f;
|
||||
/**
|
||||
* The value PI as a float. (180 degrees)
|
||||
*/
|
||||
public static final float PI = (float) Math.PI;
|
||||
/**
|
||||
* The value 2PI as a float. (360 degrees)
|
||||
*/
|
||||
public static final float TWO_PI = 2.0f * PI;
|
||||
/**
|
||||
* The value PI/2 as a float. (90 degrees)
|
||||
*/
|
||||
public static final float HALF_PI = 0.5f * PI;
|
||||
/**
|
||||
* The value PI/4 as a float. (45 degrees)
|
||||
*/
|
||||
public static final float QUARTER_PI = 0.25f * PI;
|
||||
/**
|
||||
* The value 1/PI as a float.
|
||||
*/
|
||||
public static final float INV_PI = 1.0f / PI;
|
||||
/**
|
||||
* The value 1/(2PI) as a float.
|
||||
*/
|
||||
public static final float INV_TWO_PI = 1.0f / TWO_PI;
|
||||
/**
|
||||
* A value to multiply a degree value by, to convert it to radians.
|
||||
*/
|
||||
public static final float DEG_TO_RAD = PI / 180.0f;
|
||||
/**
|
||||
* A value to multiply a radian value by, to convert it to degrees.
|
||||
*/
|
||||
public static final float RAD_TO_DEG = 180.0f / PI;
|
||||
|
||||
/**
|
||||
* Linear interpolation from startValue to endValue by the given percent.
|
||||
* Basically: ((1 - percent) * startValue) + (percent * endValue)
|
||||
*
|
||||
* @param scale scale value to use. if 1, use endValue, if 0, use startValue.
|
||||
* @param startValue Beginning value. 0% of f
|
||||
* @param endValue ending value. 100% of f
|
||||
* @return The interpolated value between startValue and endValue.
|
||||
*/
|
||||
public static float interpolateLinear(float scale, float startValue, float endValue) {
|
||||
if (startValue == endValue) {
|
||||
return startValue;
|
||||
}
|
||||
if (scale <= 0f) {
|
||||
return startValue;
|
||||
}
|
||||
if (scale >= 1f) {
|
||||
return endValue;
|
||||
}
|
||||
return ((1f - scale) * startValue) + (scale * endValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear extrapolation from startValue to endValue by the given scale.
|
||||
* if scale is between 0 and 1 this method returns the same result as interpolateLinear
|
||||
* if the scale is over 1 the value is linearly extrapolated.
|
||||
* Note that the end value is the value for a scale of 1.
|
||||
*
|
||||
* @param scale the scale for extrapolation
|
||||
* @param startValue the starting value (scale = 0)
|
||||
* @param endValue the end value (scale = 1)
|
||||
* @return an extrapolation for the given parameters
|
||||
*/
|
||||
public static float extrapolateLinear(float scale, float startValue, float endValue) {
|
||||
return ((1f - scale) * startValue) + (scale * endValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arc cosine of a value.<br>
|
||||
* Special cases:
|
||||
* <ul><li>If fValue is smaller than -1, then the result is PI.
|
||||
* <li>If the argument is greater than 1, then the result is 0.</ul>
|
||||
*
|
||||
* @param fValue The value to arc cosine.
|
||||
* @return The angle, in radians.
|
||||
* @see Math#acos(double)
|
||||
*/
|
||||
public static float acos(float fValue) {
|
||||
if (-1.0f < fValue) {
|
||||
if (fValue < 1.0f) {
|
||||
return (float) Math.acos(fValue);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arc sine of a value.<br>
|
||||
* Special cases:
|
||||
* <ul><li>If fValue is smaller than -1, then the result is -HALF_PI.
|
||||
* <li>If the argument is greater than 1, then the result is HALF_PI.</ul>
|
||||
*
|
||||
* @param fValue The value to arc sine.
|
||||
* @return the angle in radians.
|
||||
* @see Math#asin(double)
|
||||
*/
|
||||
public static float asin(float fValue) {
|
||||
if (-1.0f < fValue) {
|
||||
if (fValue < 1.0f) {
|
||||
return (float) Math.asin(fValue);
|
||||
}
|
||||
|
||||
return HALF_PI;
|
||||
}
|
||||
|
||||
return -HALF_PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arc tangent of an angle given in radians.<br>
|
||||
*
|
||||
* @param fValue The angle, in radians.
|
||||
* @return fValue's atan
|
||||
* @see Math#atan(double)
|
||||
*/
|
||||
public static float atan(float fValue) {
|
||||
return (float) Math.atan(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* A direct call to Math.atan2.
|
||||
*
|
||||
* @param fY ordinate
|
||||
* @param fX abscissa
|
||||
* @return Math.atan2(fY, fX)
|
||||
* @see Math#atan2(double, double)
|
||||
*/
|
||||
public static float atan2(float fY, float fX) {
|
||||
return (float) Math.atan2(fY, fX);
|
||||
}
|
||||
|
||||
public static float sinh(float x) {
|
||||
return (float) Math.sinh(x);
|
||||
}
|
||||
|
||||
public static float cosh(float x) {
|
||||
return (float) Math.cosh(x);
|
||||
}
|
||||
|
||||
public static float tanh(float x) {
|
||||
return (float) Math.tanh(x);
|
||||
}
|
||||
|
||||
public static float coth(float x) {
|
||||
return (float) (1d / Math.tanh(x));
|
||||
}
|
||||
|
||||
public static float arsinh(float x) {
|
||||
return log(x + sqrt(x * x + 1f));
|
||||
}
|
||||
|
||||
public static float arcosh(float x) {
|
||||
return log(x + sqrt(x * x - 1f));
|
||||
}
|
||||
|
||||
public static float artanh(float x) {
|
||||
return 0.5f * log((1f + x) / (1f - x));
|
||||
}
|
||||
|
||||
public static float arcoth(float x) {
|
||||
return 0.5f * log((x + 1f) / (x - 1f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a fValue up. A call to Math.ceil
|
||||
*
|
||||
* @param fValue The value.
|
||||
* @return The fValue rounded up
|
||||
* @see Math#ceil(double)
|
||||
*/
|
||||
public static float ceil(float fValue) {
|
||||
return (float) Math.ceil(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cosine of an angle. Direct call to Math
|
||||
*
|
||||
* @param v The angle to cosine.
|
||||
* @return the cosine of the angle.
|
||||
* @see Math#cos(double)
|
||||
*/
|
||||
public static float cos(float v) {
|
||||
return (float) Math.cos(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sine of an angle. Direct call to Math
|
||||
*
|
||||
* @param v The angle to sine.
|
||||
* @return the sine of the angle.
|
||||
* @see Math#sin(double)
|
||||
*/
|
||||
public static float sin(float v) {
|
||||
return (float) Math.sin(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns E^fValue
|
||||
*
|
||||
* @param fValue Value to raise to a power.
|
||||
* @return The value E^fValue
|
||||
* @see Math#exp(double)
|
||||
*/
|
||||
public static float exp(float fValue) {
|
||||
return (float) Math.exp(fValue);
|
||||
}
|
||||
|
||||
public static float expm1(float fValue) {
|
||||
return (float) Math.expm1(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Absolute value of a float.
|
||||
*
|
||||
* @param fValue The value to abs.
|
||||
* @return The abs of the value.
|
||||
* @see Math#abs(float)
|
||||
*/
|
||||
public static float abs(float fValue) {
|
||||
if (fValue < 0) {
|
||||
return -fValue;
|
||||
}
|
||||
return fValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number rounded down.
|
||||
*
|
||||
* @param fValue The value to round
|
||||
* @return The given number rounded down
|
||||
* @see Math#floor(double)
|
||||
*/
|
||||
public static float floor(float fValue) {
|
||||
return (float) Math.floor(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1/sqrt(fValue)
|
||||
*
|
||||
* @param fValue The value to process.
|
||||
* @return 1/sqrt(fValue)
|
||||
* @see Math#sqrt(double)
|
||||
*/
|
||||
public static float invSqrt(float fValue) {
|
||||
return (float) (1.0f / Math.sqrt(fValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly estimate 1/sqrt(fValue).
|
||||
*
|
||||
* @param x the input value (≥0)
|
||||
* @return an approximate value for 1/sqrt(x)
|
||||
*/
|
||||
public static float fastInvSqrt(float x) {
|
||||
float halfX = 0.5f * x;
|
||||
int i = Float.floatToIntBits(x); // get bits for floating value
|
||||
i = 0x5f375a86 - (i >> 1); // gives initial guess y0
|
||||
x = Float.intBitsToFloat(i); // convert bits back to float
|
||||
x = x * (1.5f - halfX * x * x); // Newton step, repeating increases accuracy
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log base E of a value.
|
||||
*
|
||||
* @param fValue The value to log.
|
||||
* @return The log of fValue base E
|
||||
* @see Math#log(double)
|
||||
*/
|
||||
public static float log(float fValue) {
|
||||
return (float) Math.log(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number raised to an exponent power. fBase^fExponent
|
||||
*
|
||||
* @param fBase The base value (IE 2)
|
||||
* @param fExponent The exponent value (IE 3)
|
||||
* @return base raised to exponent (IE 8)
|
||||
* @see Math#pow(double, double)
|
||||
*/
|
||||
public static float pow(float fBase, float fExponent) {
|
||||
return (float) Math.pow(fBase, fExponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value squared. fValue ^ 2
|
||||
*
|
||||
* @param fValue The value to square.
|
||||
* @return The square of the given value.
|
||||
*/
|
||||
public static float sqr(float fValue) {
|
||||
return fValue * fValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square root of a given value.
|
||||
*
|
||||
* @param fValue The value to sqrt.
|
||||
* @return The square root of the given value.
|
||||
* @see Math#sqrt(double)
|
||||
*/
|
||||
public static float sqrt(float fValue) {
|
||||
return (float) Math.sqrt(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tangent of the specified angle.
|
||||
*
|
||||
* @param fValue The value to tangent, in radians.
|
||||
* @return The tangent of fValue.
|
||||
* @see Math#tan(double)
|
||||
*/
|
||||
public static float tan(float fValue) {
|
||||
return (float) Math.tan(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise
|
||||
*
|
||||
* @param iValue The integer to examine.
|
||||
* @return The integer's sign.
|
||||
*/
|
||||
public static int sign(int iValue) {
|
||||
return Integer.compare(iValue, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise
|
||||
*
|
||||
* @param fValue The float to examine.
|
||||
* @return The float's sign.
|
||||
*/
|
||||
public static float sign(float fValue) {
|
||||
return Math.signum(fValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a float input and clamp it between min and max.
|
||||
*
|
||||
* @param input the value to be clamped
|
||||
* @param min the minimum output value
|
||||
* @param max the maximum output value
|
||||
* @return clamped input
|
||||
*/
|
||||
public static float clamp(float input, float min, float max) {
|
||||
return Math.max(min, Math.min(input, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps the given float to be between 0 and 1.
|
||||
*
|
||||
* @param input the value to be clamped
|
||||
* @return input clamped between 0 and 1.
|
||||
*/
|
||||
public static float saturate(float input) {
|
||||
return clamp(input, 0f, 1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if two floats are approximately equal.
|
||||
* This takes into account the magnitude of the floats, since
|
||||
* large numbers will have larger differences be close to each other.
|
||||
* <p>
|
||||
* Should return true for a=100000, b=100001, but false for a=10000, b=10001.
|
||||
*
|
||||
* @param a The first float to compare
|
||||
* @param b The second float to compare
|
||||
* @return True if a and b are approximately equal, false otherwise.
|
||||
*/
|
||||
public static boolean approximateEquals(float a, float b) {
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return (abs(a - b) / Math.max(abs(a), abs(b))) <= 0.00001f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the specified angle to lie in the interval (-pi,pi] and returns the normalized value in radians.
|
||||
*
|
||||
* @param angle the specified angle in radians
|
||||
*/
|
||||
public static float normalizeAngle(float angle) {
|
||||
final float res = angle % TWO_PI;
|
||||
if (res <= -FloatMath.PI) return res + TWO_PI;
|
||||
else if (res > FloatMath.PI) return res - TWO_PI;
|
||||
return res;
|
||||
}
|
||||
}
|
47
Projekte/common/src/main/java/pp/util/FloatPoint.java
Normal file
47
Projekte/common/src/main/java/pp/util/FloatPoint.java
Normal file
@@ -0,0 +1,47 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
/**
|
||||
* A trivial implementation of points in the plane with float coordinates.
|
||||
*
|
||||
* @param x x-coordinate
|
||||
* @param y y-coordinate
|
||||
*/
|
||||
public record FloatPoint(float x, float y) implements Position {
|
||||
public static final FloatPoint ZERO = new FloatPoint(0f, 0f);
|
||||
|
||||
/**
|
||||
* Create a new FloatPoint object for the given position.
|
||||
*
|
||||
* @param p a position
|
||||
*/
|
||||
public FloatPoint(Position p) {
|
||||
this(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x-coordinate.
|
||||
*/
|
||||
@Override
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate.
|
||||
*/
|
||||
@Override
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public static Position p(float x, float y) {
|
||||
return new FloatPoint(x, y);
|
||||
}
|
||||
}
|
77
Projekte/common/src/main/java/pp/util/Interval.java
Normal file
77
Projekte/common/src/main/java/pp/util/Interval.java
Normal file
@@ -0,0 +1,77 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import static pp.util.FloatMath.ZERO_TOLERANCE;
|
||||
import static pp.util.FloatMath.abs;
|
||||
|
||||
/**
|
||||
* Represents an interval. The start value must not be greater than the end value.
|
||||
*
|
||||
* @param from the start value of the interval
|
||||
* @param to the end value of the interval
|
||||
*/
|
||||
public record Interval(float from, float to) {
|
||||
|
||||
/**
|
||||
* Creates a new interval between two specified values.
|
||||
*
|
||||
* @param from the specified start value
|
||||
* @param to the specified end value
|
||||
*/
|
||||
public Interval {
|
||||
if (from > to)
|
||||
throw new IllegalArgumentException(from + " > " + to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this interval has length 0, i.e., from and to are equal.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return to == from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified value is contained in this interval. Note that an empty interval may
|
||||
* contain the value if the value is the start and the end value of the interval.
|
||||
*
|
||||
* @param value the specified value to check
|
||||
*/
|
||||
public boolean contains(float value) {
|
||||
return from - value <= ZERO_TOLERANCE && value - to <= ZERO_TOLERANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified interval is contained as a sub-interval.
|
||||
*
|
||||
* @param other the potential sub-interval
|
||||
*/
|
||||
public boolean contains(Interval other) {
|
||||
return from - other.from < ZERO_TOLERANCE && other.to - to < ZERO_TOLERANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this interval.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + from + "; " + to + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified interval is almost equal to this
|
||||
* interval up to the specified epsilon value.
|
||||
*
|
||||
* @param other the other interval to check
|
||||
* @param eps the allowed epsilon value
|
||||
*/
|
||||
public boolean matches(Interval other, float eps) {
|
||||
return abs(from - other.from) < eps &&
|
||||
abs(to - other.to) < eps;
|
||||
}
|
||||
}
|
84
Projekte/common/src/main/java/pp/util/Position.java
Normal file
84
Projekte/common/src/main/java/pp/util/Position.java
Normal file
@@ -0,0 +1,84 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import static pp.util.FloatMath.sqrt;
|
||||
|
||||
/**
|
||||
* Interface for all objects that provide a position in the plane.
|
||||
*/
|
||||
public interface Position extends Comparable<Position> {
|
||||
/**
|
||||
* Returns the x-coordinate of the position
|
||||
*
|
||||
* @return x-coordinate as float
|
||||
*/
|
||||
float getX();
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate of the position
|
||||
*
|
||||
* @return y-coordinate as float
|
||||
*/
|
||||
float getY();
|
||||
|
||||
/**
|
||||
* Returns the distance of this position from the specified position.
|
||||
*
|
||||
* @param x x-coordinate of the position
|
||||
* @param y y-coordinate of the position
|
||||
* @return distance
|
||||
*/
|
||||
default float distanceTo(float x, float y) {
|
||||
return sqrt(distanceSquaredTo(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance of this position from the specified position.
|
||||
* This is just a convenience method for {@linkplain #distanceTo(float, float)}.
|
||||
*
|
||||
* @param other the other position
|
||||
* @return distance
|
||||
*/
|
||||
default float distanceTo(Position other) {
|
||||
return distanceTo(other.getX(), other.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the squared distance of this position from the specified position.
|
||||
*
|
||||
* @param x x-coordinate of the position
|
||||
* @param y y-coordinate of the position
|
||||
* @return squared distance
|
||||
*/
|
||||
default float distanceSquaredTo(float x, float y) {
|
||||
final float dx = getX() - x;
|
||||
final float dy = getY() - y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the squared distance of this position from the specified position.
|
||||
*
|
||||
* @param p the other position
|
||||
* @return squared distance
|
||||
*/
|
||||
default float distanceSquaredTo(Position p) {
|
||||
return distanceSquaredTo(p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares positions in the plane from top to bottom
|
||||
* (y coordinates grow downwards) and then from left ro right.
|
||||
*/
|
||||
@Override
|
||||
default int compareTo(Position other) {
|
||||
final int c = Float.compare(getY(), other.getY());
|
||||
return c != 0 ? c : Float.compare(getX(), other.getX());
|
||||
}
|
||||
}
|
30
Projekte/common/src/main/java/pp/util/PreferencesUtils.java
Normal file
30
Projekte/common/src/main/java/pp/util/PreferencesUtils.java
Normal file
@@ -0,0 +1,30 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* A class with convenience methods for preferences.
|
||||
*/
|
||||
public class PreferencesUtils {
|
||||
private PreferencesUtils() {
|
||||
// don't instantiate
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preferences node for the specified class object. The path of the
|
||||
* preference node corresponds to the fully qualified name of the class.
|
||||
*
|
||||
* @param clazz a class object
|
||||
* @return a preference node for the specified class
|
||||
*/
|
||||
public static Preferences getPreferences(Class<?> clazz) {
|
||||
return Preferences.userNodeForPackage(clazz).node(clazz.getSimpleName());
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* An iterator that creates a random permutation of positions (x, y) where x and y are integer values
|
||||
* in the range {0, ..., width-1} x {0, ..., height-1}. All permutations are uniformly distributed
|
||||
* using the Fisher–Yates shuffle (also known as Knuth shuffle).
|
||||
*
|
||||
* @param <T> the type of elements returned by this iterator
|
||||
*/
|
||||
public class RandomPositionIterator<T> implements Iterator<T> {
|
||||
private final Random random = new Random();
|
||||
private final int height;
|
||||
private final Map<Integer, T> movedMap = new HashMap<>();
|
||||
private int remaining;
|
||||
private final Creator<T> creator;
|
||||
|
||||
/**
|
||||
* Functional interface to create instances of type T.
|
||||
*
|
||||
* @param <T> the type of elements created by this creator
|
||||
*/
|
||||
public interface Creator<T> {
|
||||
T create(int x, int y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a RandomPositionIterator for Position instances with float coordinates.
|
||||
*
|
||||
* @param width the width of the rectangle
|
||||
* @param height the height of the rectangle
|
||||
* @return a RandomPositionIterator for Position instances
|
||||
*/
|
||||
public static RandomPositionIterator<Position> floatPoints(int width, int height) {
|
||||
return new RandomPositionIterator<>(FloatPoint::new, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new permutation iterator generating a random permutation of positions (x, y)
|
||||
* where x and y are integer values in the range {0, ..., width-1} x {0, ..., height-1}.
|
||||
*
|
||||
* @param creator the creator to create instances of type T
|
||||
* @param width the width of the rectangle
|
||||
* @param height the height of the rectangle
|
||||
*/
|
||||
public RandomPositionIterator(Creator<T> creator, int width, int height) {
|
||||
this.height = height;
|
||||
this.remaining = width * height;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return remaining > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (hasNext()) {
|
||||
final int idx = random.nextInt(remaining--); // note that remaining is decremented
|
||||
final T result = getWhere(idx);
|
||||
if (idx < remaining)
|
||||
movedMap.put(idx, getWhere(remaining));
|
||||
movedMap.remove(remaining);
|
||||
return result;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
private T getWhere(int idx) {
|
||||
final T movedWhere = movedMap.get(idx);
|
||||
if (movedWhere != null)
|
||||
return movedWhere;
|
||||
final int x = idx / height;
|
||||
final int y = idx % height;
|
||||
return creator.create(x, y);
|
||||
}
|
||||
}
|
16
Projekte/common/src/main/java/pp/util/Segment.java
Normal file
16
Projekte/common/src/main/java/pp/util/Segment.java
Normal file
@@ -0,0 +1,16 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
/**
|
||||
* A directed straight line segment between two positions in the plane.
|
||||
*
|
||||
* @param from the start position of the segment
|
||||
* @param to the end position of the segment
|
||||
*/
|
||||
public record Segment(Position from, Position to) implements SegmentLike {}
|
396
Projekte/common/src/main/java/pp/util/SegmentLike.java
Normal file
396
Projekte/common/src/main/java/pp/util/SegmentLike.java
Normal file
@@ -0,0 +1,396 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import static java.lang.Float.max;
|
||||
import static java.lang.Float.min;
|
||||
import static pp.util.FloatMath.FLT_EPSILON;
|
||||
import static pp.util.FloatMath.ZERO_TOLERANCE;
|
||||
import static pp.util.FloatMath.abs;
|
||||
import static pp.util.FloatMath.atan2;
|
||||
import static pp.util.FloatMath.cos;
|
||||
import static pp.util.FloatMath.sin;
|
||||
import static pp.util.FloatMath.sqr;
|
||||
import static pp.util.FloatMath.sqrt;
|
||||
|
||||
/**
|
||||
* Interface of geometrical objects like segments, i.e., having a start and an end position.
|
||||
*/
|
||||
public interface SegmentLike {
|
||||
/**
|
||||
* Returns the start position of the segment.
|
||||
*/
|
||||
Position from();
|
||||
|
||||
/**
|
||||
* Returns the end position of the segment.
|
||||
*/
|
||||
Position to();
|
||||
|
||||
/**
|
||||
* Returns the length, i.e., the distance between the start and the end point of this segment.
|
||||
*
|
||||
* @return length of the line segment
|
||||
*/
|
||||
default float length() {
|
||||
return sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length squared where the length is the distance between the start and the
|
||||
* end point of this segment.
|
||||
*
|
||||
* @return length squared of the line segment
|
||||
*/
|
||||
default float lengthSquared() {
|
||||
return lengthSquared(from(), to());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the squared length of the vector between the specified points.
|
||||
*/
|
||||
static float lengthSquared(Position from, Position to) {
|
||||
final float dx = to.getX() - from.getX();
|
||||
final float dy = to.getY() - from.getY();
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the vector between the specified points.
|
||||
*/
|
||||
static float length(Position from, Position to) {
|
||||
return sqrt(lengthSquared(from, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the angle of this line segment with the x-axis.
|
||||
*
|
||||
* @return angle with the x-axis
|
||||
*/
|
||||
default float angle() {
|
||||
final float dx = to().getX() - from().getX();
|
||||
final float dy = to().getY() - from().getY();
|
||||
return atan2(dy, dx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance of the specified point from this segment.
|
||||
* The distance is the length of the shortest connection between the specified
|
||||
* point and any point of the line segment.
|
||||
*
|
||||
* @param x x-coordinate of the point
|
||||
* @param y y-coordinate of the point
|
||||
* @return the distance
|
||||
*/
|
||||
default float distanceTo(float x, float y) {
|
||||
return distance(from().getX(), from().getY(), to().getX(), to().getY(), x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance of the point from the segment between the specified points.
|
||||
* The distance is the length of the shortest connection between the specified
|
||||
* point and any point of the line segment.
|
||||
*
|
||||
* @param from the segment start point
|
||||
* @param to the segment end point
|
||||
* @param p the point
|
||||
* @return the distance
|
||||
*/
|
||||
static float distance(Position from, Position to, Position p) {
|
||||
return distance(from.getX(), from.getY(), to.getX(), to.getY(), p.getX(), p.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance of the point (x,y) from the segment from (x1,y1) to (x2,y2)
|
||||
* The distance is the length of the shortest connection between the specified
|
||||
* point and any point of the line segment.
|
||||
*
|
||||
* @param x1 x-coordinate of the segment start point
|
||||
* @param y1 y-coordinate of the segment start point
|
||||
* @param x2 x-coordinate of the segment end point
|
||||
* @param y2 y-coordinate of the segment end point
|
||||
* @param x x-coordinate of the point
|
||||
* @param y y-coordinate of the point
|
||||
* @return the distance
|
||||
*/
|
||||
static float distance(float x1, float y1, float x2, float y2, float x, float y) {
|
||||
final float dx = x2 - x1;
|
||||
final float dy = y2 - y1;
|
||||
final float dx1 = x - x1;
|
||||
final float dy1 = y - y1;
|
||||
if (dx * dx1 + dy * dy1 <= 0f)
|
||||
return sqrt(dx1 * dx1 + dy1 * dy1);
|
||||
final float dx2 = x - x2;
|
||||
final float dy2 = y - y2;
|
||||
if (dx * dx2 + dy * dy2 >= 0f)
|
||||
return sqrt(dx2 * dx2 + dy2 * dy2);
|
||||
final float len = sqrt(dx * dx + dy * dy);
|
||||
return FloatMath.abs(dx1 * dy - dy1 * dx) / len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance of the specified position from this segment.
|
||||
* This is just a convenience method for {@linkplain #distanceTo(float, float)}.
|
||||
*
|
||||
* @param pos a position
|
||||
* @return the distance
|
||||
*/
|
||||
default float distanceTo(Position pos) {
|
||||
return distanceTo(pos.getX(), pos.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point of this segment with the specified quotient, i.e., q*from()+(1-q)*to().
|
||||
*
|
||||
* @param q the quotient
|
||||
*/
|
||||
default Position pointAt(float q) {
|
||||
if (q == 0f) return from();
|
||||
if (q == 1f) return to();
|
||||
return new FloatPoint((1f - q) * from().getX() + q * to().getX(),
|
||||
(1f - q) * from().getY() + q * to().getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shoots a ray from the specified position in the direction of the specified angle and returns the distance
|
||||
* of the specified position from the intersection point of the ray with the straight line determined by the
|
||||
* end points of this segment. Returns {@linkplain Float#NaN} if there is no intersection.
|
||||
*
|
||||
* @param pos the specified position
|
||||
* @param angle the specified angle
|
||||
*/
|
||||
default float dist(Position pos, float angle) {
|
||||
return quotientDist(pos, quotient(pos, angle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shoots a ray from the specified position in the direction of the specified angle and returns the distance
|
||||
* of the specified position from the intersection point of the ray with the straight line determined by the
|
||||
* end points of this segment. Returns {@linkplain Float#NaN} if there is no intersection.
|
||||
*
|
||||
* @param pos the specified position
|
||||
* @param angle the specified angle
|
||||
*/
|
||||
default float dist(Position pos, Angle angle) {
|
||||
return quotientDist(pos, quotient(pos, angle.x, angle.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shoots a ray from the specified position in the direction of the point of this segment with the specified
|
||||
* quotient, i.e., the point at q*from()+(1-q)*to(), and returns the distance
|
||||
* of the specified position from the intersection point of the ray with the straight line determined by the
|
||||
* end points of this segment. Returns {@linkplain Float#NaN} if there is no intersection.
|
||||
*
|
||||
* @param pos the specified position
|
||||
* @param q the specified quotient
|
||||
*/
|
||||
default float quotientDist(Position pos, float q) {
|
||||
final float dx = (1f - q) * from().getX() + q * to().getX() - pos.getX();
|
||||
final float dy = (1f - q) * from().getY() + q * to().getY() - pos.getY();
|
||||
return sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shoots a ray from the specified position in the direction of the specified angle and returns the
|
||||
* quotient q such that the intersection point of the ray with the straight line determined by the
|
||||
* end points of this segment is at q*from()+(1-q)*to().
|
||||
*
|
||||
* @param pos the specified position
|
||||
* @param angle the specified angle
|
||||
*/
|
||||
default float quotient(Position pos, float angle) {
|
||||
final float ux = cos(angle);
|
||||
final float uy = sin(angle);
|
||||
return quotient(pos, ux, uy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shoots a ray from the specified position in the direction of the specified vector and returns the
|
||||
* quotient q such that the intersection point of the ray with the straight line determined by the
|
||||
* end points of this segment is at q*from()+(1-q)*to().
|
||||
*
|
||||
* @param pos the specified position
|
||||
* @param ux the vectors x value
|
||||
* @param uy the vectors y value
|
||||
*/
|
||||
private float quotient(Position pos, float ux, float uy) {
|
||||
final float nom = nominator(pos, ux, uy);
|
||||
final float det = determinant(ux, uy);
|
||||
// the following is for dealing with floating point imprecision
|
||||
if (abs(det) > FLT_EPSILON)
|
||||
return nom / det;
|
||||
if (abs(nom) > FLT_EPSILON)
|
||||
return Float.NaN;
|
||||
final float q = project(pos);
|
||||
if (q > -FLT_EPSILON && q - 1f < FLT_EPSILON)
|
||||
// pos lies (almost) within the segment
|
||||
return q;
|
||||
final float distFrom = isCandidate(pos, ux, uy, from());
|
||||
final float distTo = isCandidate(pos, ux, uy, to());
|
||||
if (distFrom >= 0f) {
|
||||
if (distTo >= 0f)
|
||||
return distFrom < distTo ? 0f : 1f;
|
||||
else
|
||||
return 0f;
|
||||
}
|
||||
if (distTo >= 0f)
|
||||
return 1f;
|
||||
return Float.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the determinant of a specified vector.
|
||||
*
|
||||
* @param ux the vectors x value
|
||||
* @param uy the vectors y value
|
||||
*/
|
||||
private float determinant(float ux, float uy) {
|
||||
return diffX() * uy - diffY() * ux;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nominator of the specified vector starting at a specified position.
|
||||
*
|
||||
* @param pos the specified position
|
||||
* @param ux the vectors x value
|
||||
* @param uy the vectors y value
|
||||
*/
|
||||
private float nominator(Position pos, float ux, float uy) {
|
||||
final float dx = pos.getX() - from().getX();
|
||||
final float dy = pos.getY() - from().getY();
|
||||
return dx * uy - dy * ux;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the (ux,uy) ray starting at pos hits (or almost hits) target.
|
||||
*
|
||||
* @param pos the specified start position
|
||||
* @param ux the rays x value
|
||||
* @param uy the rays y value
|
||||
* @param target the specified target position
|
||||
*/
|
||||
private float isCandidate(Position pos, float ux, float uy, Position target) {
|
||||
final float lambda = lambda(pos, target, ux, uy);
|
||||
if (lambda < -FLT_EPSILON) return -1f;
|
||||
final float dx = target.getX() - pos.getX() - lambda * ux;
|
||||
final float dy = target.getY() - pos.getY() - lambda * uy;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
private float lambda(Position p1, Position p2, float ux, float uy) {
|
||||
return ux * (p2.getX() - p1.getX()) + uy * (p2.getY() - p1.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient q such that the specified point projected onto this
|
||||
* segment is at q*from()+(1-q)*to().
|
||||
*
|
||||
* @param pos the specified points position
|
||||
*/
|
||||
default float project(Position pos) {
|
||||
return ((pos.getX() - from().getX()) * diffX() + (pos.getY() - from().getY()) * diffY()) / lengthSquared();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interval of quotients between leftAngle and rightAngle
|
||||
* looking from the specified position. The starting point of the
|
||||
* segment must be left of its end point when looking from the
|
||||
* specified position.
|
||||
*
|
||||
* @param pos the specified position to look from
|
||||
* @param leftAngle the specified left angle
|
||||
* @param rightAngle the specified right angle
|
||||
*/
|
||||
default Interval interval(Position pos, Angle leftAngle, Angle rightAngle) {
|
||||
final float nomLeft = nominator(pos, leftAngle.x, leftAngle.y);
|
||||
final float detLeft = determinant(leftAngle.x, leftAngle.y);
|
||||
final float nomRight = nominator(pos, rightAngle.x, rightAngle.y);
|
||||
final float detRight = determinant(rightAngle.x, rightAngle.y);
|
||||
if (abs(detLeft) <= FLT_EPSILON || abs(detRight) <= FLT_EPSILON)
|
||||
return new Interval(0f, 0f);
|
||||
final float q1 = nomLeft / detLeft;
|
||||
final float q2 = nomRight / detRight;
|
||||
if (q1 > q2)
|
||||
return new Interval(0f, 0f);
|
||||
final float lower = q1 < ZERO_TOLERANCE ? 0f : min(1f, q1);
|
||||
final float upper = q2 > 1f - ZERO_TOLERANCE ? 1f : max(0f, q2);
|
||||
return new Interval(lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x-coordinate of the vector from the start to the end point.
|
||||
*/
|
||||
default float diffX() {
|
||||
return to().getX() - from().getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate of the vector from the start to the end point.
|
||||
*/
|
||||
default float diffY() {
|
||||
return to().getY() - from().getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the determinant of the matrix whose first column vector is this segment and the
|
||||
* second column vector is the specified segment.
|
||||
*
|
||||
* @param other the specified segment
|
||||
*/
|
||||
default float determinantWith(SegmentLike other) {
|
||||
return diffX() * other.diffY() - diffY() * other.diffX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the square of the minimal distance between this and the specified segment.
|
||||
*
|
||||
* @param other other segment
|
||||
* @return squared distance
|
||||
*/
|
||||
default float minDistanceSquared(SegmentLike other) {
|
||||
final float rx = other.from().getX() - from().getX();
|
||||
final float ry = other.from().getY() - from().getY();
|
||||
final float ux = diffX();
|
||||
final float uy = diffY();
|
||||
final float vx = other.diffX();
|
||||
final float vy = other.diffY();
|
||||
|
||||
final float ru = rx * ux + ry * uy;
|
||||
final float rv = rx * vx + ry * vy;
|
||||
final float uu = ux * ux + uy * uy;
|
||||
final float uv = ux * vx + uy * vy;
|
||||
final float vv = vx * vx + vy * vy;
|
||||
|
||||
if (uu < ZERO_TOLERANCE) { // this segment is in fact a single point
|
||||
if (vv < ZERO_TOLERANCE) // other is a point, too
|
||||
return rx * rx + ry * ry;
|
||||
else
|
||||
return sqr(other.distanceTo(from()));
|
||||
}
|
||||
if (vv < ZERO_TOLERANCE) // other is in fact a point
|
||||
return sqr(distanceTo(other.from()));
|
||||
|
||||
final float det = uu * vv - uv * uv;
|
||||
final float s;
|
||||
final float t;
|
||||
|
||||
if (det < ZERO_TOLERANCE * uu * vv) {
|
||||
s = min(max(ru / uu, 0f), 1f);
|
||||
t = 0f;
|
||||
}
|
||||
else {
|
||||
s = min(max((ru * vv - rv * uv) / det, 0f), 1f);
|
||||
t = min(max((ru * uv - rv * uu) / det, 0f), 1f);
|
||||
}
|
||||
|
||||
final float mu1 = min(max((t * uv + ru) / uu, 0f), 1f);
|
||||
final float mu2 = min(max((s * uv - rv) / vv, 0f), 1f);
|
||||
|
||||
return pointAt(mu1).distanceSquaredTo(other.pointAt(mu2));
|
||||
}
|
||||
}
|
90
Projekte/common/src/main/java/pp/util/Util.java
Normal file
90
Projekte/common/src/main/java/pp/util/Util.java
Normal file
@@ -0,0 +1,90 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A class with auxiliary functions.
|
||||
*/
|
||||
public class Util {
|
||||
private Util() { /* do not instantiate */ }
|
||||
|
||||
/**
|
||||
* Calculates and returns the area of a polygon defined by a list of points, using the shoelace formula.
|
||||
* The result is positive if the sequence of vertices is in clockwise order, and negative
|
||||
* otherwise.
|
||||
*
|
||||
* @param points A list of positions that define the vertices of the polygon in planar coordinates.
|
||||
* @return The calculated area of the polygon.
|
||||
*/
|
||||
public static float getArea(List<? extends Position> points) {
|
||||
float sum = 0;
|
||||
Position prev = getLast(points);
|
||||
for (var next : points) {
|
||||
sum += prev.getX() * next.getY() - next.getX() * prev.getY();
|
||||
prev = next;
|
||||
}
|
||||
return 0.5f * sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last element of the given list.
|
||||
*
|
||||
* @param list the list from which to retrieve the last element
|
||||
* @param <T> the type of elements in the list
|
||||
* @return the last element of the list
|
||||
* @throws IndexOutOfBoundsException if the list is empty
|
||||
*/
|
||||
public static <T> T getLast(List<T> list) {
|
||||
return list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the order of elements in the given list.
|
||||
*
|
||||
* @param list the list to be reversed
|
||||
* @param <T> the type of elements in the list
|
||||
* @return a new list with elements in reversed order
|
||||
*/
|
||||
public static <T> List<T> reverse(List<T> list) {
|
||||
final List<T> reversed = new ArrayList<>(list);
|
||||
Collections.reverse(reversed);
|
||||
return reversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given list.
|
||||
*
|
||||
* @param list the list to be copied, may be null
|
||||
* @param <T> the type of elements in the list
|
||||
* @return a new list containing the elements of the original list, or null if the original list is null
|
||||
*/
|
||||
public static <T> List<T> copy(List<T> list) {
|
||||
return list == null ? null : new ArrayList<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to a set and returns a new set containing the original elements and the new element.
|
||||
*
|
||||
* @param set the original set, must not be null
|
||||
* @param element the element to be added to the set
|
||||
* @param <T> the type of elements in the set
|
||||
* @param <E> the type of the element being added, must extend T
|
||||
* @return a new set containing the original elements and the new element
|
||||
*/
|
||||
public static <T, E extends T> Set<T> add(Set<T> set, E element) {
|
||||
final Set<T> newSet = new HashSet<>(set);
|
||||
newSet.add(element);
|
||||
return newSet;
|
||||
}
|
||||
}
|
286
Projekte/common/src/main/java/pp/util/config/Config.java
Normal file
286
Projekte/common/src/main/java/pp/util/config/Config.java
Normal file
@@ -0,0 +1,286 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Abstract base class for representing configurations that can be read from properties files.
|
||||
* Subclasses can define fields annotated with {@link Property} to specify the configuration keys
|
||||
* and optionally {@link Separator} to specify custom separators for array values.
|
||||
*/
|
||||
public abstract class Config {
|
||||
private static final String BLANK_SEQ = " *";
|
||||
|
||||
/**
|
||||
* Annotation for specifying the property key for a field.
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(FIELD)
|
||||
@Documented
|
||||
protected @interface Property {
|
||||
/**
|
||||
* The key of the property.
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotation for specifying a custom separator for array values.
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(FIELD)
|
||||
@Documented
|
||||
protected @interface Separator {
|
||||
/**
|
||||
* The separator for array values.
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = System.getLogger(Config.class.getName());
|
||||
private static final Map<Class<?>, Function<String, ?>> CONVERTER_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
CONVERTER_MAP.put(String.class, x -> x);
|
||||
CONVERTER_MAP.put(byte.class, Byte::parseByte);
|
||||
CONVERTER_MAP.put(short.class, Short::parseShort);
|
||||
CONVERTER_MAP.put(int.class, Integer::parseInt);
|
||||
CONVERTER_MAP.put(long.class, Long::parseLong);
|
||||
CONVERTER_MAP.put(boolean.class, Boolean::parseBoolean);
|
||||
CONVERTER_MAP.put(float.class, Float::parseFloat);
|
||||
CONVERTER_MAP.put(double.class, Double::parseDouble);
|
||||
CONVERTER_MAP.put(Byte.class, Byte::parseByte);
|
||||
CONVERTER_MAP.put(Short.class, Short::parseShort);
|
||||
CONVERTER_MAP.put(Integer.class, Integer::parseInt);
|
||||
CONVERTER_MAP.put(Long.class, Long::parseLong);
|
||||
CONVERTER_MAP.put(Boolean.class, Boolean::parseBoolean);
|
||||
CONVERTER_MAP.put(Float.class, Float::parseFloat);
|
||||
CONVERTER_MAP.put(Double.class, Double::parseDouble);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified properties file and sets the values of this config using {@link #readFrom(Properties)}.
|
||||
*
|
||||
* @param file the properties file to read
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @see #readFrom(Properties)
|
||||
*/
|
||||
public void readFrom(File file) throws IOException {
|
||||
try (Reader reader = new FileReader(file)) {
|
||||
final Properties properties = new Properties();
|
||||
properties.load(reader);
|
||||
readFrom(properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values of fields annotated with {@link Property} using the specified properties.
|
||||
* Array fields can be split into components using the default separator (",") or a custom separator
|
||||
* specified with {@link Separator}.
|
||||
*
|
||||
* @param props the properties to read from
|
||||
*/
|
||||
public void readFrom(Properties props) {
|
||||
for (Class<?> clazz = getClass(); Config.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
final Property keyAnnot = field.getAnnotation(Property.class);
|
||||
if (keyAnnot != null && props.containsKey(keyAnnot.value())) {
|
||||
try {
|
||||
final String text = props.getProperty(keyAnnot.value());
|
||||
final Object value = createValue(text, field);
|
||||
setField(field, value);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
LOGGER.log(Level.ERROR, "Cannot access " + field, ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified properties file and sets the values of this config if the file exists,
|
||||
* otherwise uses default values. This method is a convenience version of {@link #readFrom(File)}
|
||||
* that checks the existence of the specified file and does nothing if the file does not exist.
|
||||
*
|
||||
* @param file the properties file to read, if it exists
|
||||
*/
|
||||
public void readFromIfExists(File file) {
|
||||
if (!file.exists()) {
|
||||
LOGGER.log(Level.INFO, "There is no config file {0}; using default configuration", //NON-NLS
|
||||
file.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
readFrom(file);
|
||||
LOGGER.log(Level.INFO, "Successfully read config from {0}", file.getAbsolutePath()); //NON-NLS
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Cannot read config file " + file.getAbsolutePath(), e); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string value from the properties file into an object that can be assigned to the specified field.
|
||||
* For array fields, the string value is first split into components.
|
||||
*
|
||||
* @param value the string value to convert
|
||||
* @param field the field to set with the converted value
|
||||
* @return an object of the appropriate type for the field
|
||||
*/
|
||||
private Object createValue(String value, Field field) {
|
||||
if (!field.getType().isArray())
|
||||
return convertToType(value, field.getType());
|
||||
// the field is an array
|
||||
final Separator sepAnn = field.getDeclaredAnnotation(Separator.class);
|
||||
final String sep = sepAnn == null ? "," : sepAnn.value();
|
||||
final String[] split = value.split(BLANK_SEQ + sep + BLANK_SEQ, -1);
|
||||
final Object array = Array.newInstance(field.getType().componentType(), split.length);
|
||||
for (int i = 0; i < split.length; i++)
|
||||
Array.set(array, i, convertToType(split[i], field.getType().componentType()));
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string value into an object of the specified type.
|
||||
*
|
||||
* @param value the string value to convert
|
||||
* @param targetType the target type to convert to
|
||||
* @return an object of the specified type
|
||||
*/
|
||||
protected Object convertToType(String value, Class<?> targetType) {
|
||||
Function<String, ?> handler = CONVERTER_MAP.get(targetType);
|
||||
if (handler != null)
|
||||
return handler.apply(value);
|
||||
throw new IllegalArgumentException("Cannot translate " + value + " to " + targetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the configuration object, including all properties and their values.
|
||||
*
|
||||
* @return a string representation of the configuration object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
final List<String> propertyStrings = getPropertyStrings();
|
||||
propertyStrings.sort(String.CASE_INSENSITIVE_ORDER);
|
||||
return "[\n" + String.join(",\n", propertyStrings) + "\n]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all property strings of the configuration object.
|
||||
*
|
||||
* @return a list of property strings
|
||||
*/
|
||||
private List<String> getPropertyStrings() {
|
||||
final List<String> propertyStrings = new ArrayList<>();
|
||||
for (Class<?> clazz = getClass(); Config.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
final String stringRepresentation = getStringRepresentation(field);
|
||||
if (stringRepresentation != null)
|
||||
propertyStrings.add(stringRepresentation);
|
||||
}
|
||||
}
|
||||
return propertyStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the string representation of a field annotated with {@link Property}.
|
||||
*
|
||||
* @param field the field to retrieve the string representation for
|
||||
* @return the string representation of the field, or null if the field is not annotated with {@link Property}
|
||||
*/
|
||||
private String getStringRepresentation(Field field) {
|
||||
final Property keyAnnotation = field.getAnnotation(Property.class);
|
||||
if (keyAnnotation != null) {
|
||||
try {
|
||||
final Object fieldValue = getField(field);
|
||||
final String valueString = asString(fieldValue);
|
||||
return keyAnnotation.value() + " -> " + field.getName() + " = " + valueString;
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
LOGGER.log(Level.ERROR, "Cannot access " + field, e); //NON-NLS
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to its string representation. For arrays, string representations of their components are produced.
|
||||
*
|
||||
* @param value the object to convert
|
||||
* @return the string representation of the object
|
||||
*/
|
||||
private String asString(Object value) {
|
||||
if (value == null)
|
||||
return "null"; //NON-NLS
|
||||
if (!value.getClass().isArray())
|
||||
return value.toString();
|
||||
final int length = Array.getLength(value);
|
||||
final List<String> components = new ArrayList<>(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
final Object component = Array.get(value, i);
|
||||
components.add(asString(component));
|
||||
}
|
||||
return "{" + String.join(", ", components) + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a field, making it accessible if necessary.
|
||||
*
|
||||
* @param field the field to set
|
||||
* @param value the value to set
|
||||
* @throws IllegalAccessException if the field cannot be accessed
|
||||
*/
|
||||
private void setField(Field field, Object value) throws IllegalAccessException {
|
||||
boolean inaccessible = !field.canAccess(this);
|
||||
if (inaccessible)
|
||||
field.setAccessible(true);
|
||||
field.set(this, value);
|
||||
if (inaccessible)
|
||||
field.setAccessible(false);
|
||||
LOGGER.log(Level.TRACE, "Set {0} to {1}", field, value); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a field, making it accessible if necessary.
|
||||
*
|
||||
* @param field the field to retrieve the value from
|
||||
* @return the value of the field
|
||||
* @throws IllegalAccessException if the field cannot be accessed
|
||||
*/
|
||||
private Object getField(Field field) throws IllegalAccessException {
|
||||
boolean inaccessible = !field.canAccess(this);
|
||||
if (inaccessible)
|
||||
field.setAccessible(true);
|
||||
final Object value = field.get(this);
|
||||
if (inaccessible)
|
||||
field.setAccessible(false);
|
||||
return value;
|
||||
}
|
||||
}
|
109
Projekte/common/src/test/java/pp/util/AngleTest.java
Normal file
109
Projekte/common/src/test/java/pp/util/AngleTest.java
Normal file
@@ -0,0 +1,109 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static pp.util.FloatMath.DEG_TO_RAD;
|
||||
import static pp.util.FloatMath.ZERO_TOLERANCE;
|
||||
import static pp.util.FloatMath.cos;
|
||||
import static pp.util.FloatMath.sin;
|
||||
|
||||
public class AngleTest {
|
||||
@Test
|
||||
public void compareAngles() {
|
||||
for (int i = 0; i < 360; i++) {
|
||||
final Angle u = Angle.fromDegrees(i);
|
||||
for (int j = 0; j < 360; j++) {
|
||||
final Angle v = Angle.fromDegrees(j);
|
||||
assertEquals("compare " + i + "° and " + j + "°", Integer.compare(i, j), (Object) u.compareTo(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addAngles() {
|
||||
for (int i = 0; i < 360; i++) {
|
||||
final Angle u = Angle.fromDegrees(i);
|
||||
for (int j = 0; j < 360; j++) {
|
||||
final Angle v = Angle.fromDegrees(j);
|
||||
final Angle sum = u.plus(v);
|
||||
assertEquals(i + "° + " + j + "°, x coordinate", cos((i + j) * DEG_TO_RAD), sum.x, ZERO_TOLERANCE);
|
||||
assertEquals(i + "° + " + j + "°, y coordinate", sin((i + j) * DEG_TO_RAD), sum.y, ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subtractAngles() {
|
||||
for (int i = 0; i < 360; i++) {
|
||||
final Angle u = Angle.fromDegrees(i);
|
||||
for (int j = 0; j < 360; j++) {
|
||||
final Angle v = Angle.fromDegrees(j);
|
||||
final Angle diff = u.minus(v);
|
||||
assertEquals(i + "° - " + j + "°, x coordinate", cos((i - j) * DEG_TO_RAD), diff.x, ZERO_TOLERANCE);
|
||||
assertEquals(i + "° - " + j + "°, y coordinate", sin((i - j) * DEG_TO_RAD), diff.y, ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minAngle() {
|
||||
for (int i = 0; i < 360; i++) {
|
||||
final Angle u = Angle.fromDegrees(i);
|
||||
for (int j = 0; j < 360; j++) {
|
||||
final Angle v = Angle.fromDegrees(j);
|
||||
final Angle diff = Angle.min(u, v);
|
||||
assertEquals(i + "° - " + j + "°, x coordinate", cos(min(i, j) * DEG_TO_RAD), diff.x, ZERO_TOLERANCE);
|
||||
assertEquals(i + "° - " + j + "°, y coordinate", sin(min(i, j) * DEG_TO_RAD), diff.y, ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bisector() {
|
||||
for (int right = 0; right < 360; right++) {
|
||||
final Angle rightAngle = Angle.fromDegrees(right);
|
||||
for (int add = 0; add < 360; add++) {
|
||||
final int left = right + add;
|
||||
final Angle bisector = Angle.fromDegrees(left).bisector(rightAngle);
|
||||
final float exp = (right + 0.5f * add) * DEG_TO_RAD;
|
||||
assertEquals("left=" + left + "° / right=" + right + "°, x coordinate", cos(exp), bisector.x, ZERO_TOLERANCE);
|
||||
assertEquals("left=" + left + "° / right=" + right + "°, y coordinate", sin(exp), bisector.y, ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void leftOf() {
|
||||
for (int right = 0; right < 360; right++) {
|
||||
final Angle rightAngle = Angle.fromDegrees(right);
|
||||
for (int add = 1; add < 360; add++)
|
||||
if (add != 180) {
|
||||
final int left = right + add;
|
||||
final Angle leftAngle = Angle.fromDegrees(left);
|
||||
assertEquals(left + "° left of " + right + "°", add < 180, leftAngle.leftOf(rightAngle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rightOf() {
|
||||
for (int right = 0; right < 360; right++) {
|
||||
final Angle rightAngle = Angle.fromDegrees(right);
|
||||
for (int add = 1; add < 360; add++)
|
||||
if (add != 180) {
|
||||
final int left = right + add;
|
||||
final Angle leftAngle = Angle.fromDegrees(left);
|
||||
assertEquals(left + "° right of " + right + "°", add > 180, leftAngle.rightOf(rightAngle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
Projekte/common/src/test/java/pp/util/IntervalTest.java
Normal file
39
Projekte/common/src/test/java/pp/util/IntervalTest.java
Normal file
@@ -0,0 +1,39 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static pp.util.FloatMath.ZERO_TOLERANCE;
|
||||
|
||||
public class IntervalTest {
|
||||
private Interval interval;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
interval = new Interval(0f, 1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contains() {
|
||||
assertTrue(interval.contains(0.5f));
|
||||
assertTrue(interval.contains(0f));
|
||||
assertTrue(interval.contains(1f));
|
||||
assertFalse(interval.contains(1.5f));
|
||||
assertFalse(interval.contains(-0.5f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matches() {
|
||||
assertTrue(interval.matches(new Interval(0f, 1f), ZERO_TOLERANCE));
|
||||
assertFalse(interval.matches(new Interval(0f, 0.99f), ZERO_TOLERANCE));
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RandomPositionIteratorTest {
|
||||
public static final int WIDTH = 15;
|
||||
public static final int HEIGHT = 25;
|
||||
|
||||
@Test
|
||||
public void permutation() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
final List<Position> permutation = new ArrayList<>();
|
||||
RandomPositionIterator.floatPoints(WIDTH, HEIGHT).forEachRemaining(permutation::add);
|
||||
assertEquals(WIDTH * HEIGHT, permutation.size());
|
||||
assertEquals(permutation.size(), new HashSet<>(permutation).size());
|
||||
for (Position w : permutation) {
|
||||
assertTrue(w.toString(), 0 <= w.getX() && w.getX() < WIDTH);
|
||||
assertTrue(w.toString(), 0 <= w.getY() && w.getY() < HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
201
Projekte/common/src/test/java/pp/util/SegmentTest.java
Normal file
201
Projekte/common/src/test/java/pp/util/SegmentTest.java
Normal file
@@ -0,0 +1,201 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static pp.util.FloatMath.FLT_EPSILON;
|
||||
import static pp.util.FloatMath.PI;
|
||||
import static pp.util.FloatMath.ZERO_TOLERANCE;
|
||||
import static pp.util.FloatMath.cos;
|
||||
import static pp.util.FloatMath.sqr;
|
||||
import static pp.util.FloatMath.sqrt;
|
||||
import static pp.util.FloatMath.tan;
|
||||
|
||||
public class SegmentTest {
|
||||
private static final float SQRT2 = sqrt(2f);
|
||||
private static final Segment segment1 = new Segment(new FloatPoint(1f, -1f), new FloatPoint(1f, 1f));
|
||||
private static final Segment segment2 = new Segment(new FloatPoint(SQRT2, 0f), new FloatPoint(0f, SQRT2));
|
||||
private static final Segment segment3 = new Segment(new FloatPoint(2f, -1f), new FloatPoint(2f, 1f));
|
||||
private static final Segment segment4 = new Segment(new FloatPoint(SQRT2, -1f), new FloatPoint(SQRT2, 1f));
|
||||
private static final Segment segment5 = new Segment(new FloatPoint(-SQRT2, 2f * SQRT2), new FloatPoint(2f * SQRT2, -SQRT2));
|
||||
private static final Segment segment6 = new Segment(new FloatPoint(0f, 0f), new FloatPoint(SQRT2, 1f));
|
||||
private static final FloatPoint ZERO = new FloatPoint(0f, 0f);
|
||||
public static final FloatPoint ONE_UP = new FloatPoint(0f, 1f);
|
||||
public static final FloatPoint ONE_DOWN = new FloatPoint(0f, -1f);
|
||||
|
||||
@Test
|
||||
public void dist1() {
|
||||
assertEquals(1f, segment1.dist(ZERO, 0f), FLT_EPSILON);
|
||||
assertEquals(1f, segment1.dist(ONE_UP, 0f), FLT_EPSILON);
|
||||
assertEquals(1f, segment1.dist(ONE_DOWN, 0f), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment1.dist(ZERO, 0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment1.dist(ONE_UP, 0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment1.dist(ONE_DOWN, 0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment1.dist(ZERO, -0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment1.dist(ONE_UP, -0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment1.dist(ONE_DOWN, -0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(SQRT2, segment1.dist(ZERO, 0.25f * PI), FLT_EPSILON);
|
||||
assertEquals(SQRT2, segment1.dist(ZERO, -0.25f * PI), FLT_EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dist2() {
|
||||
assertEquals(1f, segment2.dist(ZERO, 0.25f * PI), FLT_EPSILON);
|
||||
assertEquals(SQRT2, segment2.dist(ZERO, 0f), FLT_EPSILON);
|
||||
assertEquals(SQRT2, segment2.dist(ZERO, 0.5f * PI), FLT_EPSILON);
|
||||
assertEquals(1f / cos(0.125f * PI), segment2.dist(ZERO, 0.375f * PI), FLT_EPSILON);
|
||||
assertEquals(SQRT2, segment2.dist(ZERO, PI / 2f), FLT_EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quotient1() {
|
||||
assertEquals(0.5f, segment1.quotient(ZERO, 0f), FLT_EPSILON);
|
||||
assertEquals(1f, segment1.quotient(ONE_UP, 0f), FLT_EPSILON);
|
||||
assertEquals(0f, segment1.quotient(ONE_DOWN, 0f), FLT_EPSILON);
|
||||
assertEquals(0.5f * tan(0.125f * PI) + 0.5f, segment1.quotient(ZERO, 0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(0.5f * tan(0.125f * PI) + 1f, segment1.quotient(ONE_UP, 0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(0.5f * tan(0.125f * PI), segment1.quotient(ONE_DOWN, 0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(0.5f - 0.5f * tan(0.125f * PI), segment1.quotient(ZERO, -0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f - 0.5f * tan(0.125f * PI), segment1.quotient(ONE_UP, -0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(-0.5f * tan(0.125f * PI), segment1.quotient(ONE_DOWN, -0.125f * PI), FLT_EPSILON);
|
||||
assertEquals(1f, segment1.quotient(ZERO, 0.25f * PI), FLT_EPSILON);
|
||||
assertEquals(0f, segment1.quotient(ZERO, -0.25f * PI), FLT_EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quotient2() {
|
||||
assertEquals(0.5f, segment2.quotient(ZERO, 0.25f * PI), FLT_EPSILON);
|
||||
assertEquals(0f, segment2.quotient(ZERO, 0f), FLT_EPSILON);
|
||||
assertEquals(1f, segment2.quotient(ZERO, 0.5f * PI), FLT_EPSILON);
|
||||
assertEquals(0.5f * SQRT2, segment2.quotient(ZERO, 0.375f * PI), FLT_EPSILON);
|
||||
assertEquals(1f - 0.5f * SQRT2, segment2.quotient(ZERO, 0.125f * PI), FLT_EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void project() {
|
||||
assertEquals(0.5f, segment1.project(ZERO), FLT_EPSILON);
|
||||
assertEquals(0.5f, segment2.project(ZERO), FLT_EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minDistanceSquared1() {
|
||||
assertEquals(0f, segment1.minDistanceSquared(segment1), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment1.minDistanceSquared(segment2), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment2.minDistanceSquared(segment1), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(1f, segment1.minDistanceSquared(segment3), ZERO_TOLERANCE);
|
||||
assertEquals(1f, segment3.minDistanceSquared(segment1), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(sqr(SQRT2 - 1f), segment1.minDistanceSquared(segment4), ZERO_TOLERANCE);
|
||||
assertEquals(sqr(SQRT2 - 1f), segment4.minDistanceSquared(segment1), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment1.minDistanceSquared(segment5), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment5.minDistanceSquared(segment1), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment1.minDistanceSquared(segment6), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment6.minDistanceSquared(segment1), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment2.minDistanceSquared(segment2), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(sqr(2f - SQRT2), segment2.minDistanceSquared(segment3), ZERO_TOLERANCE);
|
||||
assertEquals(sqr(2f - SQRT2), segment3.minDistanceSquared(segment2), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment2.minDistanceSquared(segment4), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment4.minDistanceSquared(segment2), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment2.minDistanceSquared(segment5), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment5.minDistanceSquared(segment2), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment2.minDistanceSquared(segment6), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment6.minDistanceSquared(segment2), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment3.minDistanceSquared(segment3), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(sqr(2f - SQRT2), segment3.minDistanceSquared(segment4), ZERO_TOLERANCE);
|
||||
assertEquals(sqr(2f - SQRT2), segment4.minDistanceSquared(segment3), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment3.minDistanceSquared(segment5), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment5.minDistanceSquared(segment3), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(sqr(2f - SQRT2), segment3.minDistanceSquared(segment6), ZERO_TOLERANCE);
|
||||
assertEquals(sqr(2f - SQRT2), segment6.minDistanceSquared(segment3), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment4.minDistanceSquared(segment4), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment4.minDistanceSquared(segment5), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment5.minDistanceSquared(segment4), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment4.minDistanceSquared(segment6), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment6.minDistanceSquared(segment4), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment5.minDistanceSquared(segment5), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment5.minDistanceSquared(segment6), ZERO_TOLERANCE);
|
||||
assertEquals(0f, segment6.minDistanceSquared(segment5), ZERO_TOLERANCE);
|
||||
|
||||
assertEquals(0f, segment6.minDistanceSquared(segment6), ZERO_TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minDistanceSquared2() {
|
||||
final Segment s1 = new Segment(new FloatPoint(0f, 0f), new FloatPoint(2f, 1f));
|
||||
for (int i = -20; i <= 40; i++) {
|
||||
final float x = i * 0.1f;
|
||||
final Segment s2 = new Segment(new FloatPoint(x, 2f), new FloatPoint(x + 2f, -2f));
|
||||
final float dist;
|
||||
if (i <= -10)
|
||||
dist = 0.8f * sqr(1f + x);
|
||||
else if (i <= 15)
|
||||
dist = 0f;
|
||||
else
|
||||
dist = 0.8f * sqr(x - 1.5f);
|
||||
assertEquals("x = " + x, dist, s1.minDistanceSquared(s2), ZERO_TOLERANCE);
|
||||
assertEquals("x = " + x, dist, s2.minDistanceSquared(s1), ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minDistanceSquared3() {
|
||||
final Segment s1 = new Segment(new FloatPoint(0f, 0f), new FloatPoint(2f, 1f));
|
||||
for (float i = -30; i <= 30; i++) {
|
||||
final float x = i * 0.1f;
|
||||
final Segment s2 = new Segment(new FloatPoint(x, 0.5f * x), new FloatPoint(x + 2f, 0.5f * x + 1f));
|
||||
final float dist;
|
||||
if (i <= -20)
|
||||
dist = 1.25f * sqr(2f + x);
|
||||
else if (i <= 20)
|
||||
dist = 0f;
|
||||
else
|
||||
dist = 1.25f * sqr(x - 2f);
|
||||
assertEquals("x = " + x, dist, s1.minDistanceSquared(s2), ZERO_TOLERANCE);
|
||||
assertEquals("x = " + x, dist, s2.minDistanceSquared(s1), ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minDistanceSquared4() {
|
||||
final Segment s1 = new Segment(new FloatPoint(0f, 0f), new FloatPoint(3f, 1.5f));
|
||||
for (float i = -30; i <= 50; i++) {
|
||||
final float x = i * 0.1f;
|
||||
final float y = 1f - 0.5f * x;
|
||||
final Segment s2 = new Segment(new FloatPoint(x, 1f), new FloatPoint(x, 1f));
|
||||
final float dist;
|
||||
if (i <= -5)
|
||||
dist = sqr(x) + 1f;
|
||||
else if (i <= 32)
|
||||
dist = 0.2f * sqr(x - 2f);
|
||||
else
|
||||
dist = sqr(x - 3f) + 0.25f;
|
||||
assertEquals("x = " + x, dist, s1.minDistanceSquared(s2), ZERO_TOLERANCE);
|
||||
assertEquals("x = " + x, dist, s2.minDistanceSquared(s1), ZERO_TOLERANCE);
|
||||
}
|
||||
}
|
||||
}
|
101
Projekte/common/src/test/java/pp/util/config/ConfigTest.java
Normal file
101
Projekte/common/src/test/java/pp/util/config/ConfigTest.java
Normal file
@@ -0,0 +1,101 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util.config;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ConfigTest {
|
||||
|
||||
private TestConfig config;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
config = new TestConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFromProperties() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("test.string", "hello"); //NON-NLS
|
||||
properties.setProperty("test.int", "42");
|
||||
properties.setProperty("test.boolean", "true"); //NON-NLS
|
||||
properties.setProperty("test.intArray", "1; 2 ;3;4");
|
||||
|
||||
config.readFrom(properties);
|
||||
|
||||
assertEquals("hello", config.getTestString());
|
||||
assertEquals(42, config.getTestInt());
|
||||
assertTrue(config.isTestBoolean());
|
||||
assertArrayEquals(new int[]{1, 2, 3, 4}, config.getTestIntArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFromFile() throws IOException {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("test.string", "fileTest");
|
||||
properties.setProperty("test.int", "24");
|
||||
properties.setProperty("test.boolean", "false"); //NON-NLS
|
||||
properties.setProperty("test.intArray", "10;20;30");
|
||||
|
||||
File tempFile = File.createTempFile("testConfig", ".properties");
|
||||
try (FileWriter writer = new FileWriter(tempFile)) {
|
||||
properties.store(writer, null);
|
||||
}
|
||||
|
||||
config.readFrom(tempFile);
|
||||
|
||||
assertEquals("fileTest", config.getTestString());
|
||||
assertEquals(24, config.getTestInt());
|
||||
assertFalse(config.isTestBoolean());
|
||||
assertArrayEquals(new int[]{10, 20, 30}, config.getTestIntArray());
|
||||
|
||||
// Clean up
|
||||
tempFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToType() {
|
||||
assertEquals(42, config.convertToType("42", int.class));
|
||||
assertEquals(true, config.convertToType("true", boolean.class)); //NON-NLS
|
||||
assertEquals(3.14, config.convertToType("3.14", double.class));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testConvertToTypeWithUnsupportedType() {
|
||||
config.convertToType("unsupported", Object.class); //NON-NLS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("test.string", "stringValue");
|
||||
properties.setProperty("test.int", "123");
|
||||
properties.setProperty("test.boolean", "true"); //NON-NLS
|
||||
properties.setProperty("test.intArray", "5;6;7");
|
||||
|
||||
config.readFrom(properties);
|
||||
|
||||
String expected = "[\ntest.boolean -> testBoolean = true,\n" +
|
||||
"test.int -> testInt = 123,\n" +
|
||||
"test.intArray -> testIntArray = {5, 6, 7},\n" +
|
||||
"test.string -> testString = stringValue\n" +
|
||||
"]";
|
||||
assertEquals(expected, config.toString());
|
||||
}
|
||||
}
|
40
Projekte/common/src/test/java/pp/util/config/TestConfig.java
Normal file
40
Projekte/common/src/test/java/pp/util/config/TestConfig.java
Normal file
@@ -0,0 +1,40 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.util.config;
|
||||
|
||||
class TestConfig extends Config {
|
||||
@Property("test.string")
|
||||
private String testString;
|
||||
|
||||
@Property("test.int")
|
||||
private int testInt;
|
||||
|
||||
@Property("test.boolean")
|
||||
private boolean testBoolean;
|
||||
|
||||
@Property("test.intArray")
|
||||
@Separator(";")
|
||||
private int[] testIntArray;
|
||||
|
||||
// Getters for testing
|
||||
public String getTestString() {
|
||||
return testString;
|
||||
}
|
||||
|
||||
public int getTestInt() {
|
||||
return testInt;
|
||||
}
|
||||
|
||||
public boolean isTestBoolean() {
|
||||
return testBoolean;
|
||||
}
|
||||
|
||||
public int[] getTestIntArray() {
|
||||
return testIntArray;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user