Gruppe-02-clone/Projekte/common/src/main/java/pp/util/Angle.java
2024-09-18 17:04:31 +02:00

258 lines
7.0 KiB
Java

////////////////////////////////////////
// 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() + "°";
}
}